soaspec 0.2.10 → 0.2.11
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/ChangeLog +4 -0
- data/images/basic_demo.gif +0 -0
- data/lib/soaspec.rb +1 -1
- data/lib/soaspec/demo.rb +1 -1
- data/lib/soaspec/exchange/exchange.rb +97 -0
- data/lib/soaspec/exchange/exchange_extractor.rb +59 -0
- data/lib/soaspec/exchange/exchange_properties.rb +29 -0
- data/lib/soaspec/exchange/exchange_repeater.rb +19 -0
- data/lib/soaspec/exchange/request_builder.rb +57 -0
- data/lib/soaspec/exchange/variable_storer.rb +22 -0
- data/lib/soaspec/exchange_handlers/handler_accessors.rb +2 -1
- data/lib/soaspec/exchange_handlers/rest_parameters.rb +1 -1
- data/lib/soaspec/o_auth2.rb +12 -0
- data/lib/soaspec/test_server/invoices.rb +2 -1
- data/lib/soaspec/version.rb +1 -1
- metadata +8 -4
- data/lib/soaspec/exchange.rb +0 -236
- data/lib/soaspec/exchange_properties.rb +0 -27
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f4316417650edcdef9d66fb391ff90736a400dc2
|
4
|
+
data.tar.gz: 163b3ca2c9a547915693d33a7e7147dbc079cea6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2a58886e537a7e063f4977a184cac67d32ffe4827d4331315475d780450d0025bf86a000978457a0d5b52f1361e54496d1661f70904f6aa84f9ada7a8d300f12
|
7
|
+
data.tar.gz: 41c07147d1db749aab3b3c4dfbfa638b46e46297ddbdc39c9f8eef0eb27bc9ad47dc347920c960fa1069236641aa2acd58d4619c6f46f0d775783b933b8dcbfb
|
data/ChangeLog
CHANGED
data/images/basic_demo.gif
CHANGED
Binary file
|
data/lib/soaspec.rb
CHANGED
@@ -15,7 +15,7 @@ require 'soaspec/template_reader'
|
|
15
15
|
require 'soaspec/exchange_handlers/soap_handler'
|
16
16
|
require 'soaspec/exchange_handlers/exchange_handler'
|
17
17
|
require 'soaspec/exchange_handlers/rest_methods'
|
18
|
-
require 'soaspec/exchange'
|
18
|
+
require 'soaspec/exchange/exchange'
|
19
19
|
require 'soaspec/matchers'
|
20
20
|
require 'soaspec/soaspec_shared_examples'
|
21
21
|
require 'soaspec/core_ext/hash'
|
data/lib/soaspec/demo.rb
CHANGED
@@ -0,0 +1,97 @@
|
|
1
|
+
require_relative '../../soaspec'
|
2
|
+
require_relative 'exchange_properties'
|
3
|
+
require_relative 'exchange_extractor'
|
4
|
+
require_relative 'request_builder'
|
5
|
+
require_relative 'exchange_repeater'
|
6
|
+
require_relative 'variable_storer'
|
7
|
+
|
8
|
+
# This represents a request / response pair
|
9
|
+
# Essentially, params in the exchange that are set are related to the request
|
10
|
+
# What is returned is related to the response
|
11
|
+
class Exchange
|
12
|
+
extend Soaspec::ExchangeProperties
|
13
|
+
include Soaspec::ExchangeExtractor
|
14
|
+
include Soaspec::RequestBuilder
|
15
|
+
include Soaspec::ExchangeRepeater
|
16
|
+
include Soaspec::VariableStorer
|
17
|
+
|
18
|
+
# Instance of ExchangeHandler for which this exchange is made
|
19
|
+
attr_accessor :exchange_handler
|
20
|
+
# How many times to retry for a success
|
21
|
+
attr_accessor :retry_count
|
22
|
+
# Name used for displaying class
|
23
|
+
attr_accessor :test_name
|
24
|
+
# Expect Factory to fail upon trying to create
|
25
|
+
attr_writer :fail_factory
|
26
|
+
# Parameters to override for default params
|
27
|
+
attr_accessor :override_parameters
|
28
|
+
|
29
|
+
# Set retry for success variable to true so that request will be retried
|
30
|
+
# for retry_count until it's true
|
31
|
+
def retry_for_success
|
32
|
+
@retry_for_success = true
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
# @return [Bool] Whether to keep making request until success code reached
|
37
|
+
def retry_for_success?
|
38
|
+
@retry_for_success
|
39
|
+
end
|
40
|
+
|
41
|
+
# @return [Boolean] Soaspec::ExchangeHandler used by this exchange
|
42
|
+
def default_handler_used
|
43
|
+
nil
|
44
|
+
end
|
45
|
+
|
46
|
+
# @param [Symbol, String] name Name shown in RSpec run
|
47
|
+
# @param [Hash] override_parameters Parameters to override for default params
|
48
|
+
def initialize(name = self.class.to_s, override_parameters = {})
|
49
|
+
self.test_name ||= name.to_s
|
50
|
+
# As a last resort this uses the global parameter. The handler should be set straight before an exchange is made to use this
|
51
|
+
@exchange_handler ||= default_handler_used || Soaspec.api_handler
|
52
|
+
raise '@exchange_handler not set. Set either with `Soaspec.api_handler = Handler.new` or within the exchange' unless @exchange_handler
|
53
|
+
|
54
|
+
@fail_factory = nil
|
55
|
+
@override_parameters = override_parameters
|
56
|
+
@retry_for_success = false
|
57
|
+
self.retry_count = 3
|
58
|
+
@exchange_handler.elements.each { |element| methods_for_element(element) }
|
59
|
+
end
|
60
|
+
|
61
|
+
# Make request to handler with parameters defined
|
62
|
+
# Will retry until success code reached if retry_for_success? is set
|
63
|
+
# @return [Response] Response from Api handler
|
64
|
+
def make_request
|
65
|
+
Soaspec::SpecLogger.info 'Example ' + test_name
|
66
|
+
request_params = @override_parameters
|
67
|
+
(1..retry_count).each do |count|
|
68
|
+
response = exchange_handler.make_request(request_params)
|
69
|
+
return response unless retry_for_success?
|
70
|
+
return response if (200..299).cover? exchange_handler.status_code_for(response)
|
71
|
+
|
72
|
+
sleep 0.5
|
73
|
+
break response if count == retry_count
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Name describing this class when used with `RSpec.describe`
|
78
|
+
# This will make the request and store the response
|
79
|
+
# @return [String] Name given when initializing
|
80
|
+
def to_s
|
81
|
+
test_name
|
82
|
+
end
|
83
|
+
|
84
|
+
# Returns response object from Api. Will make the request if not made and then cache it for later on
|
85
|
+
# @example For SOAP it will be a Savon response
|
86
|
+
# response.body (body of response as Hash)
|
87
|
+
# response.header (head of response as Hash)
|
88
|
+
# @example For REST it will be a RestClient::Response
|
89
|
+
def response
|
90
|
+
Soaspec.last_exchange = self
|
91
|
+
@response ||= make_request
|
92
|
+
@response.define_singleton_method(:exchange) { Soaspec.last_exchange } unless @response.respond_to?(:exchange)
|
93
|
+
@response
|
94
|
+
end
|
95
|
+
|
96
|
+
alias call response
|
97
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Soaspec
|
2
|
+
# Methods for extracting aspects of the traffic for a request / response
|
3
|
+
# in an exchange from the ExchangeHandler that it's tied to
|
4
|
+
module ExchangeExtractor
|
5
|
+
# @param [String] path XPath, JSONPath to extract value
|
6
|
+
# @param [String] attribute Attribute to obtain from XML element
|
7
|
+
# @return [Array] List of values found at path
|
8
|
+
def values_from_path(path, attribute: nil)
|
9
|
+
exchange_handler.values_from_path(response, path, attribute: attribute)
|
10
|
+
end
|
11
|
+
|
12
|
+
# Request of API call. Either intended request or actual request
|
13
|
+
def request
|
14
|
+
exchange_handler.request(@response)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Get status code from api class. This is http response for Web Api
|
18
|
+
# @return [Integer] Status code from api class
|
19
|
+
def status_code
|
20
|
+
exchange_handler.status_code_for(response)
|
21
|
+
end
|
22
|
+
|
23
|
+
# @return [Boolean] Whether an element exists at the path
|
24
|
+
def element?(path)
|
25
|
+
self[path]
|
26
|
+
true
|
27
|
+
rescue NoElementAtPath
|
28
|
+
false
|
29
|
+
end
|
30
|
+
|
31
|
+
# Extract value from path api class
|
32
|
+
# @param [Object] path Path to return element for api class E.g - for SOAP this is XPath string. For JSON, this is Hash dig Array
|
33
|
+
# @return [String] Value at path
|
34
|
+
def [](path)
|
35
|
+
exchange_handler.value_from_path(response, path.to_s)
|
36
|
+
end
|
37
|
+
|
38
|
+
# @param [String] element Element to define methods for
|
39
|
+
def methods_for_element(element)
|
40
|
+
element_name = element.to_s.split('__custom_path_').last
|
41
|
+
define_singleton_method(element_name) do
|
42
|
+
exchange_handler.__send__(element, response) # Forward the call onto handler to retrieve the element for the response
|
43
|
+
end
|
44
|
+
define_singleton_method("#{element_name}?") do
|
45
|
+
begin
|
46
|
+
__send__ element_name
|
47
|
+
true
|
48
|
+
rescue NoElementAtPath
|
49
|
+
false
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# @return [Hash] Hash representing the response of the API
|
55
|
+
def to_hash
|
56
|
+
exchange_handler.to_hash(response)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Soaspec
|
2
|
+
# Convenience methods to set Exchange specific properties
|
3
|
+
# Will be used when creating a subclass of Exchange
|
4
|
+
module ExchangeProperties
|
5
|
+
# Set default exchange handler for this exchange
|
6
|
+
# This is helpful for when you need a new exchange handler created for each exchange
|
7
|
+
# @param [Class] handler_class Class of ExchangeHandler to set Exchange to use
|
8
|
+
# @param [String] name Name to call handler when it's instantiated (Defaults to class name)
|
9
|
+
# @param [Hash] params Hash of parameters to set for instance of ExchangeHandler
|
10
|
+
def default_handler(handler_class, name = handler_class.to_s, params = '')
|
11
|
+
define_method('default_handler_used') do
|
12
|
+
params_used = Hash[params.map do |k, param|
|
13
|
+
[k, param.is_a?(String) ? ERB.new(param).result(binding) : param]
|
14
|
+
end]
|
15
|
+
handler_class.new name, params_used
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Set retry_for_success to true, retrying response until a successful status code is returned
|
20
|
+
def expect_positive_status(retry_count: 3)
|
21
|
+
define_method('retry_count') do
|
22
|
+
retry_count
|
23
|
+
end
|
24
|
+
define_method('retry_for_success?') do
|
25
|
+
true
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Soaspec
|
2
|
+
# Ways of repeating an exchange to reach a desired outcome
|
3
|
+
module ExchangeRepeater
|
4
|
+
# Wait until the passed block returns true
|
5
|
+
# @param [Hash] opts Options for this instance
|
6
|
+
# @option opts [Numeric] :timeout (5) Seconds to wait before timing out.
|
7
|
+
# @option opts [Numeric] :interval (0.2) Seconds to sleep between polls.
|
8
|
+
# @option opts [String] :message Exception message if timed out.
|
9
|
+
# @option opts [Array, Exception] :ignore Exceptions to ignore while polling (default: Error::NoSuchElementError)
|
10
|
+
# @return [Self] Returns itself so operations can be done on the exchange after it's done waiting
|
11
|
+
def until(opts = {}, &script)
|
12
|
+
Soaspec::Wait.until(opts) do
|
13
|
+
@response = nil # Reset response so it can be made repeatedly
|
14
|
+
instance_eval(&script)
|
15
|
+
end
|
16
|
+
self
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Soaspec
|
2
|
+
# Methods to help build a Request
|
3
|
+
module RequestBuilder
|
4
|
+
# Specify a url to add onto the base_url of the ExchangeHandler used
|
5
|
+
# @param [String] url Url to add onto the base_url of the ExchangeHandler used
|
6
|
+
def suburl=(url)
|
7
|
+
@override_parameters[:suburl] = url
|
8
|
+
end
|
9
|
+
|
10
|
+
# Specify HTTP method to use. Default is :post
|
11
|
+
# @param [Symbol] method HTTP method. E.g, :get, :patch
|
12
|
+
def method=(method)
|
13
|
+
@override_parameters[:method] = method
|
14
|
+
end
|
15
|
+
|
16
|
+
# Set a parameter request in the request body.
|
17
|
+
# Can be used to build a request over several steps (e.g Cucumber)
|
18
|
+
# Will be used with FactoryBot
|
19
|
+
def []=(key, value)
|
20
|
+
@override_parameters[:body] ||= {}
|
21
|
+
@override_parameters[:body][key] = value
|
22
|
+
end
|
23
|
+
|
24
|
+
# Implement undefined setter with []= for FactoryBot to use without needing to define params to set
|
25
|
+
# @param [Object] method_name Name of method not defined
|
26
|
+
# @param [Object] args Arguments passed to method
|
27
|
+
# @param [Object] block
|
28
|
+
def method_missing(method_name, *args, &block)
|
29
|
+
set_value = args.first
|
30
|
+
if method_name[-1] == '=' # A setter method
|
31
|
+
getter_name = method_name[0..-2]
|
32
|
+
if set_value.class < Exchange # This would be prerequisite exchange
|
33
|
+
define_singleton_method(getter_name) { set_value }
|
34
|
+
self[getter_name] = set_value.id if set_value.respond_to?(:id)
|
35
|
+
else
|
36
|
+
self[getter_name] = set_value
|
37
|
+
end
|
38
|
+
else
|
39
|
+
super
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Used for setters that are not defined
|
44
|
+
def respond_to_missing?(method_name, *args)
|
45
|
+
method_name[-1] == '=' || super
|
46
|
+
end
|
47
|
+
|
48
|
+
# Makes request, caching the response and returning self
|
49
|
+
# Used by FactoryBot
|
50
|
+
# @return [Self]
|
51
|
+
def save!
|
52
|
+
@retry_for_success = @fail_factory ? false : true
|
53
|
+
call
|
54
|
+
self
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Soaspec
|
2
|
+
# Enables Exchange to store and retrieve variables so that one
|
3
|
+
# exchange can make assertions based on a previous one
|
4
|
+
module VariableStorer
|
5
|
+
# Stores a value in the api handler that can be accessed by the provided name
|
6
|
+
# @param [Symbol] name Name of method to use to access this value within handler
|
7
|
+
# @param [String] value Path to value to store
|
8
|
+
def store(name, value)
|
9
|
+
exchange_handler.store(name, self[value])
|
10
|
+
end
|
11
|
+
|
12
|
+
# Retrieve the stored value from the Api Handler
|
13
|
+
# @param [String, Symbol] name Name of value to retrieve
|
14
|
+
# @return [Object] value from the Api Handler stored previously
|
15
|
+
def retrieve(name)
|
16
|
+
method = '__stored_val__' + name.to_s
|
17
|
+
raise ArgumentError('Value not stored at ') unless exchange_handler.respond_to? method
|
18
|
+
|
19
|
+
exchange_handler.send(method)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -62,7 +62,8 @@ module Soaspec
|
|
62
62
|
# This will use the 'value_from_path' method which
|
63
63
|
# should be implemented by each ExchangeHandler
|
64
64
|
# @param [String, Symbol] name Method name used to access element
|
65
|
-
# @param [String, Symbol] path Path to find object (e.g, XPath, JSONPath)
|
65
|
+
# @param [String, Symbol] path Path to find object (e.g, XPath, JSONPath).
|
66
|
+
# For JSONPath a ',' can be put to get an element either path
|
66
67
|
def element(name, path)
|
67
68
|
define_method("__custom_path_#{name}") do |response|
|
68
69
|
value_from_path(response, path.to_s)
|
@@ -23,7 +23,7 @@ module Soaspec
|
|
23
23
|
# @!method access_token Retrieve OAuth2 access token
|
24
24
|
define_method('access_token') { oauth_obj.access_token }
|
25
25
|
# @!method instance_url Retrieve instance url from OAuth request
|
26
|
-
define_method('instance_url') { oauth_obj.
|
26
|
+
define_method('instance_url') { oauth_obj.instance_url }
|
27
27
|
end
|
28
28
|
|
29
29
|
# Pass path to YAML file containing OAuth2 parameters
|
data/lib/soaspec/o_auth2.rb
CHANGED
@@ -7,6 +7,8 @@ module Soaspec
|
|
7
7
|
@refresh_token = :always
|
8
8
|
# List of access tokens. They are mapped according to the OAuth parameters used
|
9
9
|
@access_tokens = {}
|
10
|
+
# List of instance URLs. They are mapped according to the OAuth parameters used
|
11
|
+
@instance_urls = {}
|
10
12
|
class << self
|
11
13
|
# Default token url used across entire suite
|
12
14
|
attr_accessor :token_url
|
@@ -17,6 +19,8 @@ module Soaspec
|
|
17
19
|
attr_accessor :refresh_token
|
18
20
|
# @attr [Hash] access_tokens List of access tokens. They are mapped according to the OAuth parameters used
|
19
21
|
attr_accessor :access_tokens
|
22
|
+
# List of URLs to that define the instance of an application
|
23
|
+
attr_accessor :instance_urls
|
20
24
|
end
|
21
25
|
|
22
26
|
# @attr [Hash] OAuth parameters
|
@@ -47,6 +51,14 @@ module Soaspec
|
|
47
51
|
Soaspec::SpecLogger.info request_message
|
48
52
|
end
|
49
53
|
|
54
|
+
# Retrieve instance_url according to access token response.
|
55
|
+
# Some applications have a different instance
|
56
|
+
# It's assumed this will be constant for a set of oauth parameters
|
57
|
+
# @return [String] Instance url
|
58
|
+
def instance_url
|
59
|
+
Soaspec::OAuth2.instance_urls[params] ||= response['instance_url']
|
60
|
+
end
|
61
|
+
|
50
62
|
# @return [String] Existing or new access token, dependent on refresh_token attribute
|
51
63
|
def access_token
|
52
64
|
case Soaspec::OAuth2.refresh_token
|
@@ -17,7 +17,8 @@ module Soaspec
|
|
17
17
|
{
|
18
18
|
access_token: 'TEST_TOKENiIsImtpZCI6IlRFU1QifQ.AAAABBBBRfaWQiOiJhYWQ5MjY3SIMULATE_LARGE_TOKEN3MmM5OGQ5NGE2YTU5YSIsImV4cCI6MTUyNzU3MTY4Mywic2NvcGUiOltdfQ.3OmCdW7fLZMUST_BE_ABLE_TO_HANDLEgAGaJB0lFYyhaw',
|
19
19
|
token_type: 'Bearer',
|
20
|
-
expires_in: '86399'
|
20
|
+
expires_in: '86399',
|
21
|
+
instance_url: %w[http://this_is_my_instance http://this_is_second_instance].sample
|
21
22
|
}
|
22
23
|
end
|
23
24
|
end
|
data/lib/soaspec/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: soaspec
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.11
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- SamuelGarrattIQA
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-02-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -403,7 +403,12 @@ files:
|
|
403
403
|
- lib/soaspec/core_ext/hash.rb
|
404
404
|
- lib/soaspec/cucumber/generic_steps.rb
|
405
405
|
- lib/soaspec/demo.rb
|
406
|
-
- lib/soaspec/exchange.rb
|
406
|
+
- lib/soaspec/exchange/exchange.rb
|
407
|
+
- lib/soaspec/exchange/exchange_extractor.rb
|
408
|
+
- lib/soaspec/exchange/exchange_properties.rb
|
409
|
+
- lib/soaspec/exchange/exchange_repeater.rb
|
410
|
+
- lib/soaspec/exchange/request_builder.rb
|
411
|
+
- lib/soaspec/exchange/variable_storer.rb
|
407
412
|
- lib/soaspec/exchange_handlers/exchange_handler.rb
|
408
413
|
- lib/soaspec/exchange_handlers/handler_accessors.rb
|
409
414
|
- lib/soaspec/exchange_handlers/response_extractor.rb
|
@@ -413,7 +418,6 @@ files:
|
|
413
418
|
- lib/soaspec/exchange_handlers/rest_parameters.rb
|
414
419
|
- lib/soaspec/exchange_handlers/rest_parameters_defaults.rb
|
415
420
|
- lib/soaspec/exchange_handlers/soap_handler.rb
|
416
|
-
- lib/soaspec/exchange_properties.rb
|
417
421
|
- lib/soaspec/exe_helpers.rb
|
418
422
|
- lib/soaspec/generator/.rspec.erb
|
419
423
|
- lib/soaspec/generator/.travis.yml.erb
|
data/lib/soaspec/exchange.rb
DELETED
@@ -1,236 +0,0 @@
|
|
1
|
-
require_relative '../soaspec'
|
2
|
-
require_relative 'exchange_properties'
|
3
|
-
|
4
|
-
# This represents a request / response pair
|
5
|
-
# Essentially, params in the exchange that are set are related to the request
|
6
|
-
# What is returned is related to the response
|
7
|
-
class Exchange
|
8
|
-
extend ExchangeProperties
|
9
|
-
|
10
|
-
# Instance of ExchangeHandler for which this exchange is made
|
11
|
-
attr_accessor :exchange_handler
|
12
|
-
# How many times to retry for a success
|
13
|
-
attr_accessor :retry_count
|
14
|
-
# Name used for displaying class
|
15
|
-
attr_accessor :test_name
|
16
|
-
# Expect Factory to fail upon trying to create
|
17
|
-
attr_writer :fail_factory
|
18
|
-
# Parameters to override for default params
|
19
|
-
attr_accessor :override_parameters
|
20
|
-
|
21
|
-
def values_from_path(path, attribute: nil)
|
22
|
-
exchange_handler.values_from_path(response, path, attribute: attribute)
|
23
|
-
end
|
24
|
-
|
25
|
-
# Set retry for success variable to true so that request will be retried
|
26
|
-
# for retry_count until it's true
|
27
|
-
def retry_for_success
|
28
|
-
@retry_for_success = true
|
29
|
-
self
|
30
|
-
end
|
31
|
-
|
32
|
-
# @return [Bool] Whether to keep making request until success code reached
|
33
|
-
def retry_for_success?
|
34
|
-
@retry_for_success
|
35
|
-
end
|
36
|
-
|
37
|
-
# @return [Boolean] Soaspec::ExchangeHandler used by this exchange
|
38
|
-
def default_handler_used
|
39
|
-
nil
|
40
|
-
end
|
41
|
-
|
42
|
-
# @param [String] element Element to define methods for
|
43
|
-
def methods_for_element(element)
|
44
|
-
element_name = element.to_s.split('__custom_path_').last
|
45
|
-
define_singleton_method(element_name) do
|
46
|
-
exchange_handler.__send__(element, response) # Forward the call onto handler to retrieve the element for the response
|
47
|
-
end
|
48
|
-
define_singleton_method("#{element_name}?") do
|
49
|
-
begin
|
50
|
-
__send__ element_name
|
51
|
-
true
|
52
|
-
rescue NoElementAtPath
|
53
|
-
false
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
# @param [Symbol, String] name Name shown in RSpec run
|
59
|
-
# @param [Hash] override_parameters Parameters to override for default params
|
60
|
-
def initialize(name = self.class.to_s, override_parameters = {})
|
61
|
-
self.test_name ||= name.to_s
|
62
|
-
# As a last resort this uses the global parameter. The handler should be set straight before an exchange is made to use this
|
63
|
-
@exchange_handler ||= default_handler_used || Soaspec.api_handler
|
64
|
-
raise '@exchange_handler not set. Set either with `Soaspec.api_handler = Handler.new` or within the exchange' unless @exchange_handler
|
65
|
-
|
66
|
-
@fail_factory = nil
|
67
|
-
@override_parameters = override_parameters
|
68
|
-
@retry_for_success = false
|
69
|
-
self.retry_count = 3
|
70
|
-
@exchange_handler.elements.each { |element| methods_for_element(element) }
|
71
|
-
end
|
72
|
-
|
73
|
-
# Specify a url to add onto the base_url of the ExchangeHandler used
|
74
|
-
# @param [String] url Url to add onto the base_url of the ExchangeHandler used
|
75
|
-
def suburl=(url)
|
76
|
-
@override_parameters[:suburl] = url
|
77
|
-
end
|
78
|
-
|
79
|
-
# Specify HTTP method to use. Default is :post
|
80
|
-
# @param [Symbol] method HTTP method. E.g, :get, :patch
|
81
|
-
def method=(method)
|
82
|
-
@override_parameters[:method] = method
|
83
|
-
end
|
84
|
-
|
85
|
-
# Make request to handler with parameters defined
|
86
|
-
# Will retry until success code reached if retry_for_success? is set
|
87
|
-
# @return [Response] Response from Api handler
|
88
|
-
def make_request
|
89
|
-
Soaspec::SpecLogger.info 'Example ' + test_name
|
90
|
-
request_params = @override_parameters
|
91
|
-
(1..retry_count).each do |count|
|
92
|
-
response = exchange_handler.make_request(request_params)
|
93
|
-
return response unless retry_for_success?
|
94
|
-
return response if (200..299).cover? @exchange_handler.status_code_for(response)
|
95
|
-
|
96
|
-
sleep 0.5
|
97
|
-
break response if count == retry_count
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
# Stores a value in the api handler that can be accessed by the provided name
|
102
|
-
# @param [Symbol] name Name of method to use to access this value within handler
|
103
|
-
# @param [String] value Path to value to store
|
104
|
-
def store(name, value)
|
105
|
-
exchange_handler.store(name, self[value])
|
106
|
-
end
|
107
|
-
|
108
|
-
# Retrieve the stored value from the Api Handler
|
109
|
-
# @param [String, Symbol] name Name of value to retrieve
|
110
|
-
# @return [Object] value from the Api Handler stored previously
|
111
|
-
def retrieve(name)
|
112
|
-
method = '__stored_val__' + name.to_s
|
113
|
-
raise ArgumentError('Value not stored at ') unless exchange_handler.respond_to? method
|
114
|
-
|
115
|
-
exchange_handler.send(method)
|
116
|
-
end
|
117
|
-
|
118
|
-
# Name describing this class when used with `RSpec.describe`
|
119
|
-
# This will make the request and store the response
|
120
|
-
# @return [String] Name given when initializing
|
121
|
-
def to_s
|
122
|
-
test_name
|
123
|
-
end
|
124
|
-
|
125
|
-
# Returns response object from Api. Will make the request if not made and then cache it for later on
|
126
|
-
# @example For SOAP it will be a Savon response
|
127
|
-
# response.body (body of response as Hash)
|
128
|
-
# response.header (head of response as Hash)
|
129
|
-
# @example For REST it will be a RestClient::Response
|
130
|
-
def response
|
131
|
-
Soaspec.last_exchange = self
|
132
|
-
@response ||= make_request
|
133
|
-
@response.define_singleton_method(:exchange) { Soaspec.last_exchange } unless @response.respond_to?(:exchange)
|
134
|
-
@response
|
135
|
-
end
|
136
|
-
|
137
|
-
alias call response
|
138
|
-
|
139
|
-
# Request of API call. Either intended request or actual request
|
140
|
-
def request
|
141
|
-
exchange_handler.request(@response)
|
142
|
-
end
|
143
|
-
|
144
|
-
# Get status code from api class. This is http response for Web Api
|
145
|
-
# @return [Integer] Status code from api class
|
146
|
-
def status_code
|
147
|
-
exchange_handler.status_code_for(response)
|
148
|
-
end
|
149
|
-
|
150
|
-
# Dummy request used to make a request without verifying it and ignoring WSDL errors
|
151
|
-
# @return [Boolean] Always returns true. Unless of course an unexpected exception occurs
|
152
|
-
def dummy_request
|
153
|
-
make_request
|
154
|
-
true
|
155
|
-
rescue Savon::HTTPError
|
156
|
-
puts 'Resolver error'
|
157
|
-
# This seems to occur first time IP address asks for WSDL
|
158
|
-
true
|
159
|
-
end
|
160
|
-
|
161
|
-
# @return [Boolean] Whether an element exists at the path
|
162
|
-
def element?(path)
|
163
|
-
[path]
|
164
|
-
true
|
165
|
-
rescue NoElementAtPath
|
166
|
-
false
|
167
|
-
end
|
168
|
-
|
169
|
-
# Extract value from path api class
|
170
|
-
# @param [Object] path Path to return element for api class E.g - for SOAP this is XPath string. For JSON, this is Hash dig Array
|
171
|
-
# @return [String] Value at path
|
172
|
-
def [](path)
|
173
|
-
exchange_handler.value_from_path(response, path.to_s)
|
174
|
-
end
|
175
|
-
|
176
|
-
# Set a parameter request in the request body.
|
177
|
-
# Can be used to build a request over several steps (e.g Cucumber)
|
178
|
-
# Will be used with FactoryBot
|
179
|
-
def []=(key, value)
|
180
|
-
@override_parameters[:body] ||= {}
|
181
|
-
@override_parameters[:body][key] = value
|
182
|
-
end
|
183
|
-
|
184
|
-
# Implement undefined setter with []= for FactoryBot to use without needing to define params to set
|
185
|
-
# @param [Object] method_name Name of method not defined
|
186
|
-
# @param [Object] args Arguments passed to method
|
187
|
-
# @param [Object] block
|
188
|
-
def method_missing(method_name, *args, &block)
|
189
|
-
set_value = args.first
|
190
|
-
if method_name[-1] == '=' # A setter method
|
191
|
-
getter_name = method_name[0..-2]
|
192
|
-
if set_value.class < Exchange # This would be prerequisite exchange
|
193
|
-
define_singleton_method(getter_name) { set_value }
|
194
|
-
self[getter_name] = set_value.id if set_value.respond_to?(:id)
|
195
|
-
else
|
196
|
-
self[getter_name] = set_value
|
197
|
-
end
|
198
|
-
else
|
199
|
-
super
|
200
|
-
end
|
201
|
-
end
|
202
|
-
|
203
|
-
# Used for setters that are not defined
|
204
|
-
def respond_to_missing?(method_name, *args)
|
205
|
-
method_name[-1] == '=' || super
|
206
|
-
end
|
207
|
-
|
208
|
-
# Makes request, caching the response and returning self
|
209
|
-
# Used by FactoryBot
|
210
|
-
# @return [Self]
|
211
|
-
def save!
|
212
|
-
@retry_for_success = @fail_factory ? false : true
|
213
|
-
call
|
214
|
-
self
|
215
|
-
end
|
216
|
-
|
217
|
-
# @return [Hash] Hash representing the response of the API
|
218
|
-
def to_hash
|
219
|
-
exchange_handler.to_hash(response)
|
220
|
-
end
|
221
|
-
|
222
|
-
# Wait until the passed block returns true
|
223
|
-
# @param [Hash] opts Options for this instance
|
224
|
-
# @option opts [Numeric] :timeout (5) Seconds to wait before timing out.
|
225
|
-
# @option opts [Numeric] :interval (0.2) Seconds to sleep between polls.
|
226
|
-
# @option opts [String] :message Exception mesage if timed out.
|
227
|
-
# @option opts [Array, Exception] :ignore Exceptions to ignore while polling (default: Error::NoSuchElementError)
|
228
|
-
# @return [Self] Returns itself so operations can be done on the exchange after it's done waiting
|
229
|
-
def until(opts = {}, &script)
|
230
|
-
Soaspec::Wait.until(opts) do
|
231
|
-
@response = nil # Reset response so it can be made repeatedly
|
232
|
-
instance_eval(&script)
|
233
|
-
end
|
234
|
-
self
|
235
|
-
end
|
236
|
-
end
|
@@ -1,27 +0,0 @@
|
|
1
|
-
# Convenience methods to set Exchange specific properties
|
2
|
-
# Will be used when creating a subclass of Exchange
|
3
|
-
module ExchangeProperties
|
4
|
-
# Set default exchange handler for this exchange
|
5
|
-
# This is helpful for when you need a new exchange handler created for each exchange
|
6
|
-
# @param [Class] handler_class Class of ExchangeHandler to set Exchange to use
|
7
|
-
# @param [String] name Name to call handler when it's instantiated (Defaults to class name)
|
8
|
-
# @param [Hash] params Hash of parameters to set for instance of ExchangeHandler
|
9
|
-
def default_handler(handler_class, name = handler_class.to_s, params = '')
|
10
|
-
define_method('default_handler_used') do
|
11
|
-
params_used = Hash[params.map do |k, param|
|
12
|
-
[k, param.is_a?(String) ? ERB.new(param).result(binding) : param]
|
13
|
-
end]
|
14
|
-
handler_class.new name, params_used
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
# Set retry_for_success to true, retrying response until a successful status code is returned
|
19
|
-
def expect_positive_status(retry_count: 3)
|
20
|
-
define_method('retry_count') do
|
21
|
-
retry_count
|
22
|
-
end
|
23
|
-
define_method('retry_for_success?') do
|
24
|
-
true
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|