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,44 +1,44 @@
|
|
1
|
-
module Soaspec
|
2
|
-
# Contains commonly used REST methods
|
3
|
-
module RestMethods
|
4
|
-
# Make REST Post Exchange
|
5
|
-
# @param [String] name Name of test displayed
|
6
|
-
# @param [Hash] params Exchange parameters
|
7
|
-
# @return [Exchange] Instance of Exchange class. Assertions are made by default on the response body
|
8
|
-
def post(name, params = {})
|
9
|
-
Exchange.new(name, method: :post, **params)
|
10
|
-
end
|
11
|
-
|
12
|
-
# Make REST Patch Exchange
|
13
|
-
# @param [String] name Name of test displayed
|
14
|
-
# @param [Hash] params Exchange parameters
|
15
|
-
# @return [Exchange] Instance of Exchange class. Assertions are made by default on the response body
|
16
|
-
def patch(name, params = {})
|
17
|
-
Exchange.new(name, method: :patch, **params)
|
18
|
-
end
|
19
|
-
|
20
|
-
# Make REST Put Exchange
|
21
|
-
# @param [String] name Name of test displayed
|
22
|
-
# @param [Hash] params Exchange parameters
|
23
|
-
# @return [Exchange] Instance of Exchange class. Assertions are made by default on the response body
|
24
|
-
def put(name, params = {})
|
25
|
-
Exchange.new(name, method: :put, **params)
|
26
|
-
end
|
27
|
-
|
28
|
-
# Make REST Get Exchange
|
29
|
-
# @param [String] name Name of test displayed
|
30
|
-
# @param [Hash] params Exchange parameters
|
31
|
-
# @return [Exchange] Instance of Exchange class. Assertions are made by default on the response body
|
32
|
-
def get(name, params = {})
|
33
|
-
Exchange.new(name, method: :get, **params)
|
34
|
-
end
|
35
|
-
|
36
|
-
# Make REST Delete Exchange
|
37
|
-
# @param [String] name Name of test displayed
|
38
|
-
# @param [Hash] params Exchange parameters
|
39
|
-
# @return [Exchange] Instance of Exchange class. Assertions are made by default on the response body
|
40
|
-
def delete(name, params = {})
|
41
|
-
Exchange.new(name, method: :delete, **params)
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
1
|
+
module Soaspec
|
2
|
+
# Contains commonly used REST methods
|
3
|
+
module RestMethods
|
4
|
+
# Make REST Post Exchange
|
5
|
+
# @param [String] name Name of test displayed
|
6
|
+
# @param [Hash] params Exchange parameters
|
7
|
+
# @return [Exchange] Instance of Exchange class. Assertions are made by default on the response body
|
8
|
+
def post(name, params = {})
|
9
|
+
Exchange.new(name, method: :post, **params)
|
10
|
+
end
|
11
|
+
|
12
|
+
# Make REST Patch Exchange
|
13
|
+
# @param [String] name Name of test displayed
|
14
|
+
# @param [Hash] params Exchange parameters
|
15
|
+
# @return [Exchange] Instance of Exchange class. Assertions are made by default on the response body
|
16
|
+
def patch(name, params = {})
|
17
|
+
Exchange.new(name, method: :patch, **params)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Make REST Put Exchange
|
21
|
+
# @param [String] name Name of test displayed
|
22
|
+
# @param [Hash] params Exchange parameters
|
23
|
+
# @return [Exchange] Instance of Exchange class. Assertions are made by default on the response body
|
24
|
+
def put(name, params = {})
|
25
|
+
Exchange.new(name, method: :put, **params)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Make REST Get Exchange
|
29
|
+
# @param [String] name Name of test displayed
|
30
|
+
# @param [Hash] params Exchange parameters
|
31
|
+
# @return [Exchange] Instance of Exchange class. Assertions are made by default on the response body
|
32
|
+
def get(name, params = {})
|
33
|
+
Exchange.new(name, method: :get, **params)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Make REST Delete Exchange
|
37
|
+
# @param [String] name Name of test displayed
|
38
|
+
# @param [Hash] params Exchange parameters
|
39
|
+
# @return [Exchange] Instance of Exchange class. Assertions are made by default on the response body
|
40
|
+
def delete(name, params = {})
|
41
|
+
Exchange.new(name, method: :delete, **params)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -1,86 +1,86 @@
|
|
1
|
-
module Soaspec
|
2
|
-
# Methods to define parameters specific to REST handler
|
3
|
-
module RestParameters
|
4
|
-
# Defines method 'base_url_value' containing base URL used in REST requests
|
5
|
-
# @param [String] url Base Url to use in REST requests. Suburl is appended to this
|
6
|
-
def base_url(url)
|
7
|
-
raise ArgumentError, "Base Url passed must be a String for #{self} but was #{url.class}" unless url.is_a?(String)
|
8
|
-
|
9
|
-
define_method('base_url_value') { ERB.new(url).result(binding) }
|
10
|
-
end
|
11
|
-
|
12
|
-
# Will create access_token method based on passed parameters
|
13
|
-
# @param [Hash] params OAuth 2 parameters
|
14
|
-
# @option params [token_url] URL to retrieve OAuth token from. @Note this can be set globally instead of here
|
15
|
-
# @option params [client_id] Client ID
|
16
|
-
# @option params [client_secret] Client Secret
|
17
|
-
# @option params [username] Username used in password grant
|
18
|
-
# @option params [password] Password used in password grant
|
19
|
-
# @option params [security_token] Security Token used in password grant
|
20
|
-
def oauth2(params)
|
21
|
-
# @!method oauth_obj Object to handle oauth2
|
22
|
-
define_method('oauth_obj') { OAuth2.new(params, api_username) }
|
23
|
-
# @!method access_token Retrieve OAuth2 access token
|
24
|
-
define_method('access_token') { oauth_obj.access_token }
|
25
|
-
# @!method instance_url Retrieve instance url from OAuth request
|
26
|
-
define_method('instance_url') { oauth_obj.instance_url }
|
27
|
-
end
|
28
|
-
|
29
|
-
# Pass path to YAML file containing OAuth2 parameters
|
30
|
-
# @param [String] path_to_filename Will have Soaspec.credentials_folder appended to it if set
|
31
|
-
def oauth2_file(path_to_filename)
|
32
|
-
oauth_parameters = load_credentials_hash(path_to_filename)
|
33
|
-
@client_id = oauth_parameters[:client_id] if oauth_parameters[:client_id]
|
34
|
-
oauth2 oauth_parameters
|
35
|
-
end
|
36
|
-
|
37
|
-
# @return [String] Client id obtained from credentials file
|
38
|
-
def client_id
|
39
|
-
raise '@client_id is not set. Set by specifying credentials file with "oauth2_file FILENAME" before this is called' unless @client_id
|
40
|
-
|
41
|
-
@client_id
|
42
|
-
end
|
43
|
-
|
44
|
-
# Define basic authentication
|
45
|
-
# @param [String] user Username to use
|
46
|
-
# @param [String] password Password to use
|
47
|
-
def basic_auth(user: nil, password: nil)
|
48
|
-
raise ArgumentError, "Must pass both 'user' and 'password' for #{self}" unless user && password
|
49
|
-
|
50
|
-
define_method('basic_auth_params') do
|
51
|
-
{ user: user, password: password }
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
# Pass path to YAML file containing Basic Auth parameters (i.e, both username and password)
|
56
|
-
# @param [String] path_to_filename Will have Soaspec.credentials_folder appended to it if set
|
57
|
-
def basic_auth_file(path_to_filename)
|
58
|
-
basic_auth load_credentials_hash(path_to_filename)
|
59
|
-
end
|
60
|
-
|
61
|
-
# @param [Hash] headers Hash of REST headers used in RestClient
|
62
|
-
def headers(headers)
|
63
|
-
define_method('rest_client_headers') { headers }
|
64
|
-
end
|
65
|
-
|
66
|
-
# Convert each key from snake_case to PascalCase
|
67
|
-
def pascal_keys(set)
|
68
|
-
define_method('pascal_keys?') { set }
|
69
|
-
end
|
70
|
-
|
71
|
-
private
|
72
|
-
|
73
|
-
# Load credentials hash from a YAML using Soaspec.credentials_folder if set, adding '.yml' if not set
|
74
|
-
# @return [Hash] Hash with credentials in it
|
75
|
-
def load_credentials_hash(filename)
|
76
|
-
raise ArgumentError, "Filename passed must be a String for #{self} but was #{filename.class}" unless filename.is_a?(String)
|
77
|
-
|
78
|
-
full_path = Soaspec.credentials_folder ? File.join(Soaspec.credentials_folder, filename) : filename
|
79
|
-
full_path += '.yml' unless full_path.end_with?('.yml') # Automatically add 'yml' extension
|
80
|
-
file_hash = YAML.load_file(full_path)
|
81
|
-
raise "File at #{full_path} is not a hash" unless file_hash.is_a? Hash
|
82
|
-
|
83
|
-
file_hash.transform_keys_to_symbols
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|
1
|
+
module Soaspec
|
2
|
+
# Methods to define parameters specific to REST handler
|
3
|
+
module RestParameters
|
4
|
+
# Defines method 'base_url_value' containing base URL used in REST requests
|
5
|
+
# @param [String] url Base Url to use in REST requests. Suburl is appended to this
|
6
|
+
def base_url(url)
|
7
|
+
raise ArgumentError, "Base Url passed must be a String for #{self} but was #{url.class}" unless url.is_a?(String)
|
8
|
+
|
9
|
+
define_method('base_url_value') { ERB.new(url).result(binding) }
|
10
|
+
end
|
11
|
+
|
12
|
+
# Will create access_token method based on passed parameters
|
13
|
+
# @param [Hash] params OAuth 2 parameters
|
14
|
+
# @option params [token_url] URL to retrieve OAuth token from. @Note this can be set globally instead of here
|
15
|
+
# @option params [client_id] Client ID
|
16
|
+
# @option params [client_secret] Client Secret
|
17
|
+
# @option params [username] Username used in password grant
|
18
|
+
# @option params [password] Password used in password grant
|
19
|
+
# @option params [security_token] Security Token used in password grant
|
20
|
+
def oauth2(params)
|
21
|
+
# @!method oauth_obj Object to handle oauth2
|
22
|
+
define_method('oauth_obj') { OAuth2.new(params, api_username) }
|
23
|
+
# @!method access_token Retrieve OAuth2 access token
|
24
|
+
define_method('access_token') { oauth_obj.access_token }
|
25
|
+
# @!method instance_url Retrieve instance url from OAuth request
|
26
|
+
define_method('instance_url') { oauth_obj.instance_url }
|
27
|
+
end
|
28
|
+
|
29
|
+
# Pass path to YAML file containing OAuth2 parameters
|
30
|
+
# @param [String] path_to_filename Will have Soaspec.credentials_folder appended to it if set
|
31
|
+
def oauth2_file(path_to_filename)
|
32
|
+
oauth_parameters = load_credentials_hash(path_to_filename)
|
33
|
+
@client_id = oauth_parameters[:client_id] if oauth_parameters[:client_id]
|
34
|
+
oauth2 oauth_parameters
|
35
|
+
end
|
36
|
+
|
37
|
+
# @return [String] Client id obtained from credentials file
|
38
|
+
def client_id
|
39
|
+
raise '@client_id is not set. Set by specifying credentials file with "oauth2_file FILENAME" before this is called' unless @client_id
|
40
|
+
|
41
|
+
@client_id
|
42
|
+
end
|
43
|
+
|
44
|
+
# Define basic authentication
|
45
|
+
# @param [String] user Username to use
|
46
|
+
# @param [String] password Password to use
|
47
|
+
def basic_auth(user: nil, password: nil)
|
48
|
+
raise ArgumentError, "Must pass both 'user' and 'password' for #{self}" unless user && password
|
49
|
+
|
50
|
+
define_method('basic_auth_params') do
|
51
|
+
{ user: user, password: password }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Pass path to YAML file containing Basic Auth parameters (i.e, both username and password)
|
56
|
+
# @param [String] path_to_filename Will have Soaspec.credentials_folder appended to it if set
|
57
|
+
def basic_auth_file(path_to_filename)
|
58
|
+
basic_auth load_credentials_hash(path_to_filename)
|
59
|
+
end
|
60
|
+
|
61
|
+
# @param [Hash] headers Hash of REST headers used in RestClient
|
62
|
+
def headers(headers)
|
63
|
+
define_method('rest_client_headers') { headers }
|
64
|
+
end
|
65
|
+
|
66
|
+
# Convert each key from snake_case to PascalCase
|
67
|
+
def pascal_keys(set)
|
68
|
+
define_method('pascal_keys?') { set }
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
# Load credentials hash from a YAML using Soaspec.credentials_folder if set, adding '.yml' if not set
|
74
|
+
# @return [Hash] Hash with credentials in it
|
75
|
+
def load_credentials_hash(filename)
|
76
|
+
raise ArgumentError, "Filename passed must be a String for #{self} but was #{filename.class}" unless filename.is_a?(String)
|
77
|
+
|
78
|
+
full_path = Soaspec.credentials_folder ? File.join(Soaspec.credentials_folder, filename) : filename
|
79
|
+
full_path += '.yml' unless full_path.end_with?('.yml') # Automatically add 'yml' extension
|
80
|
+
file_hash = YAML.load_file(full_path)
|
81
|
+
raise "File at #{full_path} is not a hash" unless file_hash.is_a? Hash
|
82
|
+
|
83
|
+
file_hash.transform_keys_to_symbols
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -1,21 +1,21 @@
|
|
1
|
-
module Soaspec
|
2
|
-
# Defaults for Soaspec RestParameters methods
|
3
|
-
module RestParametersDefaults
|
4
|
-
# Set through following method. Base URL in REST requests.
|
5
|
-
def base_url_value
|
6
|
-
nil
|
7
|
-
end
|
8
|
-
|
9
|
-
# Headers used in RestClient
|
10
|
-
def rest_client_headers
|
11
|
-
{}
|
12
|
-
end
|
13
|
-
|
14
|
-
# Whether to convert each key in the request to PascalCase
|
15
|
-
# It will also auto convert simple XPath, JSONPath where '//' or '..' not specified
|
16
|
-
# @return Whether to convert to PascalCase
|
17
|
-
def pascal_keys?
|
18
|
-
false
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
1
|
+
module Soaspec
|
2
|
+
# Defaults for Soaspec RestParameters methods
|
3
|
+
module RestParametersDefaults
|
4
|
+
# Set through following method. Base URL in REST requests.
|
5
|
+
def base_url_value
|
6
|
+
nil
|
7
|
+
end
|
8
|
+
|
9
|
+
# Headers used in RestClient
|
10
|
+
def rest_client_headers
|
11
|
+
{}
|
12
|
+
end
|
13
|
+
|
14
|
+
# Whether to convert each key in the request to PascalCase
|
15
|
+
# It will also auto convert simple XPath, JSONPath where '//' or '..' not specified
|
16
|
+
# @return Whether to convert to PascalCase
|
17
|
+
def pascal_keys?
|
18
|
+
false
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -1,235 +1,235 @@
|
|
1
|
-
require_relative 'exchange_handler'
|
2
|
-
require_relative '../core_ext/hash'
|
3
|
-
require_relative '../not_found_errors'
|
4
|
-
require_relative 'handler_accessors'
|
5
|
-
require_relative '../interpreter'
|
6
|
-
require 'forwardable'
|
7
|
-
|
8
|
-
module Soaspec
|
9
|
-
# Accessors specific to SOAP handler
|
10
|
-
module SoapAccessors
|
11
|
-
# Define attributes set on root SOAP element
|
12
|
-
def root_attributes(attributes)
|
13
|
-
define_method('request_root_attributes') do
|
14
|
-
attributes
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
# Wraps around Savon client defining default values dependent on the soap request
|
20
|
-
class SoapHandler < ExchangeHandler
|
21
|
-
extend Soaspec::SoapAccessors
|
22
|
-
extend Forwardable
|
23
|
-
|
24
|
-
delegate [:operations] => :client
|
25
|
-
|
26
|
-
# Savon client used to make SOAP calls
|
27
|
-
attr_accessor :client
|
28
|
-
# SOAP Operation to use by default
|
29
|
-
attr_accessor :operation
|
30
|
-
|
31
|
-
# Attributes set at the root XML element of SOAP request
|
32
|
-
def request_root_attributes
|
33
|
-
nil
|
34
|
-
end
|
35
|
-
|
36
|
-
# Options to log xml request and response
|
37
|
-
def logging_options
|
38
|
-
{
|
39
|
-
log: true, # See request and response. (Put this in traffic file)
|
40
|
-
log_level: :debug,
|
41
|
-
logger: Soaspec::SpecLogger.create,
|
42
|
-
pretty_print_xml: true # Prints XML pretty
|
43
|
-
}
|
44
|
-
end
|
45
|
-
|
46
|
-
# Default Savon options. See http://savonrb.com/version2/globals.html for details
|
47
|
-
# @example Things could go wrong if not set properly
|
48
|
-
# env_namespace: :soap, # Change environment namespace
|
49
|
-
# namespace_identifier: :tst, # Change namespace element
|
50
|
-
# element_form_default: :qualified # Populate each element with namespace
|
51
|
-
# namespace: 'http://Extended_namespace.xsd' change root namespace
|
52
|
-
# basic_auth: 'user', 'password'
|
53
|
-
# @return [Hash] Default Savon options for all BasicSoapHandler
|
54
|
-
def default_options
|
55
|
-
{
|
56
|
-
ssl_verify_mode: :none, # Easier for testing. Not so secure
|
57
|
-
follow_redirects: true, # Necessary for many API calls
|
58
|
-
soap_version: 2, # use SOAP 1.2. You will get 415 error if this is incorrect
|
59
|
-
raise_errors: false # HTTP errors not cause failure as often negative test scenarios expect not 200 response
|
60
|
-
}
|
61
|
-
end
|
62
|
-
|
63
|
-
# Add values to here when extending this class to have default Savon options.
|
64
|
-
# See http://savonrb.com/version2/globals.html for details
|
65
|
-
# @return [Hash] Savon options adding to & overriding defaults
|
66
|
-
def savon_options
|
67
|
-
{
|
68
|
-
}
|
69
|
-
end
|
70
|
-
|
71
|
-
# Setup object to handle communicating with a particular SOAP WSDL
|
72
|
-
# @param [String] name Name to describe handler. Used in calling 'to_s'
|
73
|
-
# @param [Hash] options Options defining SOAP request. WSDL, authentication, see http://savonrb.com/version2/globals.html for list of options
|
74
|
-
def initialize(name = self.class.to_s, options = {})
|
75
|
-
if name.is_a?(Hash) && options == {} # If name is not set
|
76
|
-
options = name
|
77
|
-
name = self.class.to_s
|
78
|
-
end
|
79
|
-
super
|
80
|
-
set_remove_keys(options, %i[operation default_hash template_name])
|
81
|
-
merged_options = Soaspec::SpecLogger.log_api_traffic? ? default_options.merge(logging_options) : default_options
|
82
|
-
merged_options.merge! savon_options
|
83
|
-
merged_options.merge!(options)
|
84
|
-
self.client = Savon.client(merged_options)
|
85
|
-
end
|
86
|
-
|
87
|
-
# Used in making request via hash or in template via Erb
|
88
|
-
# @param [Hash] request_parameters Hash representing elements to send in request
|
89
|
-
# If the :body key is set, this will be used as the request body
|
90
|
-
def request_body_params(request_parameters)
|
91
|
-
test_values = request_parameters[:body] || request_parameters
|
92
|
-
test_values.transform_keys_to_symbols if Soaspec.always_use_keys?
|
93
|
-
end
|
94
|
-
|
95
|
-
# Used in together with Exchange request that passes such override parameters
|
96
|
-
# @param [Hash] request_parameters Parameters used to overwrite defaults in request
|
97
|
-
def make_request(request_parameters)
|
98
|
-
test_values = request_body_params request_parameters
|
99
|
-
# Call the SOAP operation with the request XML provided
|
100
|
-
begin
|
101
|
-
if @request_option == :template
|
102
|
-
test_values = IndifferentHash.new(test_values) # Allow test_values to be either Symbol or String
|
103
|
-
client.call(operation, xml: Soaspec::TemplateReader.new.render_body(template_name, binding))
|
104
|
-
elsif @request_option == :hash
|
105
|
-
client.call(operation, message: @default_hash.merge(test_values), attributes: request_root_attributes)
|
106
|
-
end
|
107
|
-
rescue Savon::HTTPError => soap_error
|
108
|
-
soap_error
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
# @param [Hash] format Format of expected result
|
113
|
-
# @return [Object] Generic body to be displayed in error messages
|
114
|
-
def response_body(response, format: :hash)
|
115
|
-
case format
|
116
|
-
when :hash
|
117
|
-
response.body
|
118
|
-
when :raw
|
119
|
-
response.xml
|
120
|
-
else
|
121
|
-
response.body
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
# @return [Boolean] Whether the request found the desired value or not
|
126
|
-
def found?(response)
|
127
|
-
status_code_for(response) != 404
|
128
|
-
end
|
129
|
-
|
130
|
-
# Response status code for response. '200' indicates a success
|
131
|
-
# @param [Savon::Response] response
|
132
|
-
# @return [Integer] Status code
|
133
|
-
def status_code_for(response)
|
134
|
-
response.http.code
|
135
|
-
end
|
136
|
-
|
137
|
-
# @return [Boolean] Whether response includes provided string within it
|
138
|
-
def include_in_body?(response, expected)
|
139
|
-
response.to_xml.to_s.include? expected
|
140
|
-
end
|
141
|
-
|
142
|
-
# @param [Symbol] expected
|
143
|
-
# @return [Boolean] Whether response body contains expected key
|
144
|
-
def include_key?(response, expected)
|
145
|
-
body = response.body
|
146
|
-
body.extend Hashie::Extensions::DeepFind
|
147
|
-
!body.deep_find_all(expected).empty?
|
148
|
-
end
|
149
|
-
|
150
|
-
# Convert all XML nodes to lowercase
|
151
|
-
# @param [Nokogiri::XML::Document] xml_doc Xml document to convert
|
152
|
-
def convert_to_lower_case(xml_doc)
|
153
|
-
xml_doc.traverse do |node|
|
154
|
-
node.name = node.name.downcase if node.is_a?(Nokogiri::XML::Element)
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
|
-
# Returns the value at the provided xpath
|
159
|
-
# @param [Savon::Response] response
|
160
|
-
# @param [String] xpath
|
161
|
-
# @return [Enumerable] Elements found through Xpath
|
162
|
-
def xpath_elements_for(response: nil, xpath: nil, attribute: nil)
|
163
|
-
raise ArgumentError('response and xpath must be passed to method') unless response && xpath
|
164
|
-
|
165
|
-
xpath = "//*[@#{attribute}]" unless attribute.nil?
|
166
|
-
xpath = '//' + xpath if xpath[0] != '/'
|
167
|
-
temp_doc = response.doc.dup
|
168
|
-
convert_to_lower_case(temp_doc) if convert_to_lower?
|
169
|
-
if strip_namespaces? && !xpath.include?(':')
|
170
|
-
temp_doc.remove_namespaces!
|
171
|
-
temp_doc.xpath(xpath)
|
172
|
-
else
|
173
|
-
temp_doc.xpath(xpath, temp_doc.collect_namespaces)
|
174
|
-
end
|
175
|
-
end
|
176
|
-
|
177
|
-
# Based on a exchange, return the value at the provided xpath
|
178
|
-
# If the path does not begin with a '/', a '//' is added to it
|
179
|
-
# @param [Savon::Response] response
|
180
|
-
# @param [String] path Xpath
|
181
|
-
# @param [String] attribute Generic attribute to find. Will override path
|
182
|
-
# @return [String] Value at Xpath
|
183
|
-
def value_from_path(response, path, attribute: nil)
|
184
|
-
results = xpath_elements_for(response: response, xpath: path, attribute: attribute)
|
185
|
-
raise NoElementAtPath, "No value at Xpath '#{path}' in XML #{response.doc}" if results.empty?
|
186
|
-
return results.first.inner_text if attribute.nil?
|
187
|
-
|
188
|
-
results.first.attributes[attribute].inner_text
|
189
|
-
end
|
190
|
-
|
191
|
-
# @return [Enumerable] List of values returned from path
|
192
|
-
def values_from_path(response, path, attribute: nil)
|
193
|
-
xpath_elements_for(response: response, xpath: path, attribute: attribute).map(&:inner_text)
|
194
|
-
end
|
195
|
-
|
196
|
-
# alias elements xpath_elements_for
|
197
|
-
|
198
|
-
# @return [Boolean] Whether any of the keys of the Body Hash include value
|
199
|
-
def include_value?(response, expected_value)
|
200
|
-
response.body.include_value?(expected_value)
|
201
|
-
end
|
202
|
-
|
203
|
-
# Hash of response body
|
204
|
-
def to_hash(response)
|
205
|
-
response.body
|
206
|
-
end
|
207
|
-
|
208
|
-
# Convenience methods for once off usage of a SOAP request
|
209
|
-
class << self
|
210
|
-
# Implement undefined setter with []= for FactoryBot to use without needing to define params to set
|
211
|
-
# @param [Object] method_name Name of method not defined
|
212
|
-
# @param [Object] args Arguments passed to method
|
213
|
-
# @param [Object] block
|
214
|
-
def method_missing(method_name, *args, &block)
|
215
|
-
tmp_class = new(method_name)
|
216
|
-
operations = tmp_class.operations
|
217
|
-
if operations.include? method_name
|
218
|
-
tmp_class.operation = method_name
|
219
|
-
exchange = Exchange.new(method_name, *args)
|
220
|
-
exchange.exchange_handler = tmp_class
|
221
|
-
yield exchange if block_given?
|
222
|
-
exchange
|
223
|
-
else
|
224
|
-
super
|
225
|
-
end
|
226
|
-
end
|
227
|
-
|
228
|
-
def respond_to_missing?(method_name, *args)
|
229
|
-
tmp_class = new(args)
|
230
|
-
operations = tmp_class.operations
|
231
|
-
operations.include?(method_name) || super
|
232
|
-
end
|
233
|
-
end
|
234
|
-
end
|
235
|
-
end
|
1
|
+
require_relative 'exchange_handler'
|
2
|
+
require_relative '../core_ext/hash'
|
3
|
+
require_relative '../not_found_errors'
|
4
|
+
require_relative 'handler_accessors'
|
5
|
+
require_relative '../interpreter'
|
6
|
+
require 'forwardable'
|
7
|
+
|
8
|
+
module Soaspec
|
9
|
+
# Accessors specific to SOAP handler
|
10
|
+
module SoapAccessors
|
11
|
+
# Define attributes set on root SOAP element
|
12
|
+
def root_attributes(attributes)
|
13
|
+
define_method('request_root_attributes') do
|
14
|
+
attributes
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Wraps around Savon client defining default values dependent on the soap request
|
20
|
+
class SoapHandler < ExchangeHandler
|
21
|
+
extend Soaspec::SoapAccessors
|
22
|
+
extend Forwardable
|
23
|
+
|
24
|
+
delegate [:operations] => :client
|
25
|
+
|
26
|
+
# Savon client used to make SOAP calls
|
27
|
+
attr_accessor :client
|
28
|
+
# SOAP Operation to use by default
|
29
|
+
attr_accessor :operation
|
30
|
+
|
31
|
+
# Attributes set at the root XML element of SOAP request
|
32
|
+
def request_root_attributes
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
|
36
|
+
# Options to log xml request and response
|
37
|
+
def logging_options
|
38
|
+
{
|
39
|
+
log: true, # See request and response. (Put this in traffic file)
|
40
|
+
log_level: :debug,
|
41
|
+
logger: Soaspec::SpecLogger.create,
|
42
|
+
pretty_print_xml: true # Prints XML pretty
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
# Default Savon options. See http://savonrb.com/version2/globals.html for details
|
47
|
+
# @example Things could go wrong if not set properly
|
48
|
+
# env_namespace: :soap, # Change environment namespace
|
49
|
+
# namespace_identifier: :tst, # Change namespace element
|
50
|
+
# element_form_default: :qualified # Populate each element with namespace
|
51
|
+
# namespace: 'http://Extended_namespace.xsd' change root namespace
|
52
|
+
# basic_auth: 'user', 'password'
|
53
|
+
# @return [Hash] Default Savon options for all BasicSoapHandler
|
54
|
+
def default_options
|
55
|
+
{
|
56
|
+
ssl_verify_mode: :none, # Easier for testing. Not so secure
|
57
|
+
follow_redirects: true, # Necessary for many API calls
|
58
|
+
soap_version: 2, # use SOAP 1.2. You will get 415 error if this is incorrect
|
59
|
+
raise_errors: false # HTTP errors not cause failure as often negative test scenarios expect not 200 response
|
60
|
+
}
|
61
|
+
end
|
62
|
+
|
63
|
+
# Add values to here when extending this class to have default Savon options.
|
64
|
+
# See http://savonrb.com/version2/globals.html for details
|
65
|
+
# @return [Hash] Savon options adding to & overriding defaults
|
66
|
+
def savon_options
|
67
|
+
{
|
68
|
+
}
|
69
|
+
end
|
70
|
+
|
71
|
+
# Setup object to handle communicating with a particular SOAP WSDL
|
72
|
+
# @param [String] name Name to describe handler. Used in calling 'to_s'
|
73
|
+
# @param [Hash] options Options defining SOAP request. WSDL, authentication, see http://savonrb.com/version2/globals.html for list of options
|
74
|
+
def initialize(name = self.class.to_s, options = {})
|
75
|
+
if name.is_a?(Hash) && options == {} # If name is not set
|
76
|
+
options = name
|
77
|
+
name = self.class.to_s
|
78
|
+
end
|
79
|
+
super
|
80
|
+
set_remove_keys(options, %i[operation default_hash template_name])
|
81
|
+
merged_options = Soaspec::SpecLogger.log_api_traffic? ? default_options.merge(logging_options) : default_options
|
82
|
+
merged_options.merge! savon_options
|
83
|
+
merged_options.merge!(options)
|
84
|
+
self.client = Savon.client(merged_options)
|
85
|
+
end
|
86
|
+
|
87
|
+
# Used in making request via hash or in template via Erb
|
88
|
+
# @param [Hash] request_parameters Hash representing elements to send in request
|
89
|
+
# If the :body key is set, this will be used as the request body
|
90
|
+
def request_body_params(request_parameters)
|
91
|
+
test_values = request_parameters[:body] || request_parameters
|
92
|
+
test_values.transform_keys_to_symbols if Soaspec.always_use_keys?
|
93
|
+
end
|
94
|
+
|
95
|
+
# Used in together with Exchange request that passes such override parameters
|
96
|
+
# @param [Hash] request_parameters Parameters used to overwrite defaults in request
|
97
|
+
def make_request(request_parameters)
|
98
|
+
test_values = request_body_params request_parameters
|
99
|
+
# Call the SOAP operation with the request XML provided
|
100
|
+
begin
|
101
|
+
if @request_option == :template
|
102
|
+
test_values = IndifferentHash.new(test_values) # Allow test_values to be either Symbol or String
|
103
|
+
client.call(operation, xml: Soaspec::TemplateReader.new.render_body(template_name, binding))
|
104
|
+
elsif @request_option == :hash
|
105
|
+
client.call(operation, message: @default_hash.merge(test_values), attributes: request_root_attributes)
|
106
|
+
end
|
107
|
+
rescue Savon::HTTPError => soap_error
|
108
|
+
soap_error
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# @param [Hash] format Format of expected result
|
113
|
+
# @return [Object] Generic body to be displayed in error messages
|
114
|
+
def response_body(response, format: :hash)
|
115
|
+
case format
|
116
|
+
when :hash
|
117
|
+
response.body
|
118
|
+
when :raw
|
119
|
+
response.xml
|
120
|
+
else
|
121
|
+
response.body
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# @return [Boolean] Whether the request found the desired value or not
|
126
|
+
def found?(response)
|
127
|
+
status_code_for(response) != 404
|
128
|
+
end
|
129
|
+
|
130
|
+
# Response status code for response. '200' indicates a success
|
131
|
+
# @param [Savon::Response] response
|
132
|
+
# @return [Integer] Status code
|
133
|
+
def status_code_for(response)
|
134
|
+
response.http.code
|
135
|
+
end
|
136
|
+
|
137
|
+
# @return [Boolean] Whether response includes provided string within it
|
138
|
+
def include_in_body?(response, expected)
|
139
|
+
response.to_xml.to_s.include? expected
|
140
|
+
end
|
141
|
+
|
142
|
+
# @param [Symbol] expected
|
143
|
+
# @return [Boolean] Whether response body contains expected key
|
144
|
+
def include_key?(response, expected)
|
145
|
+
body = response.body
|
146
|
+
body.extend Hashie::Extensions::DeepFind
|
147
|
+
!body.deep_find_all(expected).empty?
|
148
|
+
end
|
149
|
+
|
150
|
+
# Convert all XML nodes to lowercase
|
151
|
+
# @param [Nokogiri::XML::Document] xml_doc Xml document to convert
|
152
|
+
def convert_to_lower_case(xml_doc)
|
153
|
+
xml_doc.traverse do |node|
|
154
|
+
node.name = node.name.downcase if node.is_a?(Nokogiri::XML::Element)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
# Returns the value at the provided xpath
|
159
|
+
# @param [Savon::Response] response
|
160
|
+
# @param [String] xpath
|
161
|
+
# @return [Enumerable] Elements found through Xpath
|
162
|
+
def xpath_elements_for(response: nil, xpath: nil, attribute: nil)
|
163
|
+
raise ArgumentError('response and xpath must be passed to method') unless response && xpath
|
164
|
+
|
165
|
+
xpath = "//*[@#{attribute}]" unless attribute.nil?
|
166
|
+
xpath = '//' + xpath if xpath[0] != '/'
|
167
|
+
temp_doc = response.doc.dup
|
168
|
+
convert_to_lower_case(temp_doc) if convert_to_lower?
|
169
|
+
if strip_namespaces? && !xpath.include?(':')
|
170
|
+
temp_doc.remove_namespaces!
|
171
|
+
temp_doc.xpath(xpath)
|
172
|
+
else
|
173
|
+
temp_doc.xpath(xpath, temp_doc.collect_namespaces)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
# Based on a exchange, return the value at the provided xpath
|
178
|
+
# If the path does not begin with a '/', a '//' is added to it
|
179
|
+
# @param [Savon::Response] response
|
180
|
+
# @param [String] path Xpath
|
181
|
+
# @param [String] attribute Generic attribute to find. Will override path
|
182
|
+
# @return [String] Value at Xpath
|
183
|
+
def value_from_path(response, path, attribute: nil)
|
184
|
+
results = xpath_elements_for(response: response, xpath: path, attribute: attribute)
|
185
|
+
raise NoElementAtPath, "No value at Xpath '#{path}' in XML #{response.doc}" if results.empty?
|
186
|
+
return results.first.inner_text if attribute.nil?
|
187
|
+
|
188
|
+
results.first.attributes[attribute].inner_text
|
189
|
+
end
|
190
|
+
|
191
|
+
# @return [Enumerable] List of values returned from path
|
192
|
+
def values_from_path(response, path, attribute: nil)
|
193
|
+
xpath_elements_for(response: response, xpath: path, attribute: attribute).map(&:inner_text)
|
194
|
+
end
|
195
|
+
|
196
|
+
# alias elements xpath_elements_for
|
197
|
+
|
198
|
+
# @return [Boolean] Whether any of the keys of the Body Hash include value
|
199
|
+
def include_value?(response, expected_value)
|
200
|
+
response.body.include_value?(expected_value)
|
201
|
+
end
|
202
|
+
|
203
|
+
# Hash of response body
|
204
|
+
def to_hash(response)
|
205
|
+
response.body
|
206
|
+
end
|
207
|
+
|
208
|
+
# Convenience methods for once off usage of a SOAP request
|
209
|
+
class << self
|
210
|
+
# Implement undefined setter with []= for FactoryBot to use without needing to define params to set
|
211
|
+
# @param [Object] method_name Name of method not defined
|
212
|
+
# @param [Object] args Arguments passed to method
|
213
|
+
# @param [Object] block
|
214
|
+
def method_missing(method_name, *args, &block)
|
215
|
+
tmp_class = new(method_name)
|
216
|
+
operations = tmp_class.operations
|
217
|
+
if operations.include? method_name
|
218
|
+
tmp_class.operation = method_name
|
219
|
+
exchange = Exchange.new(method_name, *args)
|
220
|
+
exchange.exchange_handler = tmp_class
|
221
|
+
yield exchange if block_given?
|
222
|
+
exchange
|
223
|
+
else
|
224
|
+
super
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
def respond_to_missing?(method_name, *args)
|
229
|
+
tmp_class = new(args)
|
230
|
+
operations = tmp_class.operations
|
231
|
+
operations.include?(method_name) || super
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|