soaspec 0.2.32 → 0.2.33
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +15 -15
- data/.gitlab-ci.yml +62 -62
- data/.rspec +3 -3
- data/.rubocop.yml +2 -2
- data/CODE_OF_CONDUCT.md +74 -74
- data/ChangeLog +632 -625
- data/Dockerfile +7 -7
- data/Gemfile +8 -8
- data/LICENSE.txt +21 -21
- data/README.md +231 -231
- data/Rakefile +52 -52
- data/Todo.md +16 -16
- data/exe/soaspec +138 -138
- data/exe/xml_to_yaml_file +43 -43
- data/lib/soaspec.rb +106 -105
- data/lib/soaspec/baseline.rb +23 -0
- data/lib/soaspec/core_ext/hash.rb +44 -44
- data/lib/soaspec/cucumber/generic_steps.rb +94 -94
- data/lib/soaspec/demo.rb +6 -6
- data/lib/soaspec/errors.rb +24 -22
- data/lib/soaspec/exchange/exchange.rb +129 -129
- data/lib/soaspec/exchange/exchange_extractor.rb +90 -90
- data/lib/soaspec/exchange/exchange_properties.rb +28 -28
- data/lib/soaspec/exchange/exchange_repeater.rb +21 -21
- data/lib/soaspec/exchange/request_builder.rb +70 -70
- data/lib/soaspec/exchange/variable_storer.rb +24 -24
- data/lib/soaspec/exchange_handlers/exchange_handler.rb +98 -98
- data/lib/soaspec/exchange_handlers/exchange_handler_defaults.rb +61 -61
- data/lib/soaspec/exchange_handlers/handler_accessors.rb +132 -132
- data/lib/soaspec/exchange_handlers/request/rest_request.rb +59 -51
- data/lib/soaspec/exchange_handlers/request/soap_request.rb +41 -41
- data/lib/soaspec/exchange_handlers/response_extractor.rb +84 -84
- data/lib/soaspec/exchange_handlers/rest_exchanger_factory.rb +111 -111
- data/lib/soaspec/exchange_handlers/rest_handler.rb +307 -298
- data/lib/soaspec/exchange_handlers/rest_methods.rb +65 -65
- data/lib/soaspec/exchange_handlers/rest_parameters.rb +112 -112
- data/lib/soaspec/exchange_handlers/rest_parameters_defaults.rb +42 -42
- data/lib/soaspec/exchange_handlers/soap_handler.rb +241 -241
- data/lib/soaspec/exe_helpers.rb +94 -94
- data/lib/soaspec/generate_server.rb +48 -48
- 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/features/support/env.rb.erb +3 -3
- data/lib/soaspec/generator/generate_exchange.html.erb +47 -47
- 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 +56 -56
- 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 +9 -9
- data/lib/soaspec/interpreter.rb +70 -70
- data/lib/soaspec/matchers.rb +140 -118
- data/lib/soaspec/o_auth2.rb +142 -142
- data/lib/soaspec/soaspec_shared_examples.rb +26 -26
- data/lib/soaspec/spec_logger.rb +143 -143
- data/lib/soaspec/template_reader.rb +30 -30
- data/lib/soaspec/test_server/bank.wsdl +90 -90
- data/lib/soaspec/test_server/get_bank.rb +166 -166
- data/lib/soaspec/test_server/id_manager.rb +41 -41
- data/lib/soaspec/test_server/invoices.rb +29 -29
- 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 +21 -21
- data/lib/soaspec/test_server/test_attribute.rb +14 -14
- data/lib/soaspec/test_server/test_namespace.rb +14 -14
- data/lib/soaspec/version.rb +6 -6
- data/lib/soaspec/virtual_server.rb +190 -176
- data/lib/soaspec/wait.rb +43 -43
- data/lib/soaspec/wsdl_generator.rb +215 -215
- data/soaspec.gemspec +58 -58
- data/test.wsdl +116 -116
- data/test.xml +10 -10
- data/test_wsdl.rb +43 -43
- metadata +4 -3
@@ -1,65 +1,65 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Soaspec
|
4
|
-
# Contains commonly used REST methods. Include this module in the spec where you want to use it
|
5
|
-
#
|
6
|
-
# TODO: For some reason from 'soaspec pry' sinatra always gets loaded instead of this
|
7
|
-
#
|
8
|
-
# @example
|
9
|
-
# include Soaspec::RestMethods
|
10
|
-
#
|
11
|
-
# context CustomExchangeHandler.new('Create Notes') do
|
12
|
-
# post(:minimum_data, body: { subject: 'Minimal' }) do
|
13
|
-
# it_behaves_like 'success scenario'
|
14
|
-
# its(:subject) { is_expected.to eq 'Minimal' }
|
15
|
-
# end
|
16
|
-
#
|
17
|
-
# post(:one_note, body: { subject: 'One', note: { 'Note 1' } }) do
|
18
|
-
# it_behaves_like 'success scenario'
|
19
|
-
# it 'has 1 note' do
|
20
|
-
# expect(get(subject.id).values_at_path('notes').count).to eq 1
|
21
|
-
# end
|
22
|
-
# end
|
23
|
-
# end
|
24
|
-
module RestMethods
|
25
|
-
# Make REST Post Exchange
|
26
|
-
# @param [String] name Name of test displayed
|
27
|
-
# @param [Hash] params Exchange parameters
|
28
|
-
# @return [Exchange] Instance of Exchange class. Assertions are made by default on the response body
|
29
|
-
def post(name, params = {})
|
30
|
-
Exchange.new(name, method: :post, **params)
|
31
|
-
end
|
32
|
-
|
33
|
-
# Make REST Patch Exchange
|
34
|
-
# @param [String] name Name of test displayed
|
35
|
-
# @param [Hash] params Exchange parameters
|
36
|
-
# @return [Exchange] Instance of Exchange class. Assertions are made by default on the response body
|
37
|
-
def patch(name, params = {})
|
38
|
-
Exchange.new(name, method: :patch, **params)
|
39
|
-
end
|
40
|
-
|
41
|
-
# Make REST Put Exchange
|
42
|
-
# @param [String] name Name of test displayed
|
43
|
-
# @param [Hash] params Exchange parameters
|
44
|
-
# @return [Exchange] Instance of Exchange class. Assertions are made by default on the response body
|
45
|
-
def put(name, params = {})
|
46
|
-
Exchange.new(name, method: :put, **params)
|
47
|
-
end
|
48
|
-
|
49
|
-
# Make REST Get Exchange
|
50
|
-
# @param [String] name Name of test displayed
|
51
|
-
# @param [Hash] params Exchange parameters
|
52
|
-
# @return [Exchange] Instance of Exchange class. Assertions are made by default on the response body
|
53
|
-
def get(name, params = {})
|
54
|
-
Exchange.new(name, method: :get, **params)
|
55
|
-
end
|
56
|
-
|
57
|
-
# Make REST Delete Exchange
|
58
|
-
# @param [String] name Name of test displayed
|
59
|
-
# @param [Hash] params Exchange parameters
|
60
|
-
# @return [Exchange] Instance of Exchange class. Assertions are made by default on the response body
|
61
|
-
def delete(name, params = {})
|
62
|
-
Exchange.new(name, method: :delete, **params)
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Soaspec
|
4
|
+
# Contains commonly used REST methods. Include this module in the spec where you want to use it
|
5
|
+
#
|
6
|
+
# TODO: For some reason from 'soaspec pry' sinatra always gets loaded instead of this
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# include Soaspec::RestMethods
|
10
|
+
#
|
11
|
+
# context CustomExchangeHandler.new('Create Notes') do
|
12
|
+
# post(:minimum_data, body: { subject: 'Minimal' }) do
|
13
|
+
# it_behaves_like 'success scenario'
|
14
|
+
# its(:subject) { is_expected.to eq 'Minimal' }
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# post(:one_note, body: { subject: 'One', note: { 'Note 1' } }) do
|
18
|
+
# it_behaves_like 'success scenario'
|
19
|
+
# it 'has 1 note' do
|
20
|
+
# expect(get(subject.id).values_at_path('notes').count).to eq 1
|
21
|
+
# end
|
22
|
+
# end
|
23
|
+
# end
|
24
|
+
module RestMethods
|
25
|
+
# Make REST Post Exchange
|
26
|
+
# @param [String] name Name of test displayed
|
27
|
+
# @param [Hash] params Exchange parameters
|
28
|
+
# @return [Exchange] Instance of Exchange class. Assertions are made by default on the response body
|
29
|
+
def post(name, params = {})
|
30
|
+
Exchange.new(name, method: :post, **params)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Make REST Patch Exchange
|
34
|
+
# @param [String] name Name of test displayed
|
35
|
+
# @param [Hash] params Exchange parameters
|
36
|
+
# @return [Exchange] Instance of Exchange class. Assertions are made by default on the response body
|
37
|
+
def patch(name, params = {})
|
38
|
+
Exchange.new(name, method: :patch, **params)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Make REST Put Exchange
|
42
|
+
# @param [String] name Name of test displayed
|
43
|
+
# @param [Hash] params Exchange parameters
|
44
|
+
# @return [Exchange] Instance of Exchange class. Assertions are made by default on the response body
|
45
|
+
def put(name, params = {})
|
46
|
+
Exchange.new(name, method: :put, **params)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Make REST Get Exchange
|
50
|
+
# @param [String] name Name of test displayed
|
51
|
+
# @param [Hash] params Exchange parameters
|
52
|
+
# @return [Exchange] Instance of Exchange class. Assertions are made by default on the response body
|
53
|
+
def get(name, params = {})
|
54
|
+
Exchange.new(name, method: :get, **params)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Make REST Delete Exchange
|
58
|
+
# @param [String] name Name of test displayed
|
59
|
+
# @param [Hash] params Exchange parameters
|
60
|
+
# @return [Exchange] Instance of Exchange class. Assertions are made by default on the response body
|
61
|
+
def delete(name, params = {})
|
62
|
+
Exchange.new(name, method: :delete, **params)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -1,112 +1,112 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Soaspec
|
4
|
-
# Methods to define parameters specific to REST handler
|
5
|
-
module RestParameters
|
6
|
-
# Defines method 'base_url_value' containing base URL used in REST requests
|
7
|
-
# @param [String] url Base Url to use in REST requests. Suburl is appended to this
|
8
|
-
def base_url(url)
|
9
|
-
raise ArgumentError, "Base Url passed must be a String for #{self} but was #{url.class}" unless url.is_a?(String)
|
10
|
-
|
11
|
-
define_method('base_url_value') { ERB.new(url).result(binding) }
|
12
|
-
# URL used if subclassing handler that sets this previously
|
13
|
-
define_singleton_method('parent_url') { ERB.new(url).result(binding) }
|
14
|
-
end
|
15
|
-
|
16
|
-
# Will create access_token method based on passed parameters
|
17
|
-
# @param [Hash] params OAuth 2 parameters
|
18
|
-
# @option params [token_url] URL to retrieve OAuth token from. @Note this can be set globally instead of here
|
19
|
-
# @option params [client_id] Client ID
|
20
|
-
# @option params [client_secret] Client Secret
|
21
|
-
# @option params [username] Username used in password grant
|
22
|
-
# @option params [password] Password used in password grant
|
23
|
-
# @option params [security_token] Security Token used in password grant
|
24
|
-
def oauth2(params)
|
25
|
-
# @!method oauth_obj Object to handle oauth2
|
26
|
-
define_method('oauth_obj') { OAuth2.new(params, api_username) }
|
27
|
-
# @!method access_token Retrieve OAuth2 access token
|
28
|
-
define_method('access_token') { oauth_obj.access_token }
|
29
|
-
# @!method instance_url Retrieve instance url from OAuth request
|
30
|
-
define_method('instance_url') { oauth_obj.instance_url }
|
31
|
-
end
|
32
|
-
|
33
|
-
# Pass path to YAML file containing OAuth2 parameters
|
34
|
-
# @param [String] path_to_filename Will have Soaspec.credentials_folder appended to it if set
|
35
|
-
def oauth2_file(path_to_filename)
|
36
|
-
oauth_parameters = load_credentials_hash(path_to_filename)
|
37
|
-
@client_id = oauth_parameters[:client_id] if oauth_parameters[:client_id]
|
38
|
-
oauth2 oauth_parameters
|
39
|
-
end
|
40
|
-
|
41
|
-
# @return [String] Client id obtained from credentials file
|
42
|
-
def client_id
|
43
|
-
raise '@client_id is not set. Set by specifying credentials file with "oauth2_file FILENAME" before this is called' unless @client_id
|
44
|
-
|
45
|
-
@client_id
|
46
|
-
end
|
47
|
-
|
48
|
-
# Define basic authentication
|
49
|
-
# @param [String] user Username to use
|
50
|
-
# @param [String] password Password to use
|
51
|
-
def basic_auth(user: nil, password: nil)
|
52
|
-
raise ArgumentError, "Must pass both 'user' and 'password' for #{self}" unless user && password
|
53
|
-
|
54
|
-
define_method('basic_auth_params') do
|
55
|
-
{ user: user, password: password }
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
# Pass path to YAML file containing Basic Auth parameters (i.e, both username and password)
|
60
|
-
# @param [String] path_to_filename Will have Soaspec.credentials_folder appended to it if set
|
61
|
-
def basic_auth_file(path_to_filename)
|
62
|
-
basic_auth load_credentials_hash(path_to_filename)
|
63
|
-
end
|
64
|
-
|
65
|
-
# @param [Hash] headers Hash of REST headers used in RestClient
|
66
|
-
def headers(headers)
|
67
|
-
define_method('rest_client_headers') { headers }
|
68
|
-
end
|
69
|
-
|
70
|
-
# Convert each key from snake_case to PascalCase
|
71
|
-
def pascal_keys(set)
|
72
|
-
define_method('pascal_keys?') { set }
|
73
|
-
end
|
74
|
-
|
75
|
-
# Pass block to perform after every response is retrieved
|
76
|
-
# @example Throw exception if response body has an 'error' element equal to true
|
77
|
-
# after_response do |response, handler|
|
78
|
-
# raise Soaspec::ResponseError if handler.value_from_path(response, 'error')
|
79
|
-
# end
|
80
|
-
def after_response
|
81
|
-
define_method('after_response') { |response, _self| yield response, self }
|
82
|
-
end
|
83
|
-
|
84
|
-
# @param [Array] exception_list List of exceptions to retry response on. Default is
|
85
|
-
# any REST exception
|
86
|
-
# @param [Integer] pause Time to wait before retrying
|
87
|
-
# @param [Integer] count Times to retry
|
88
|
-
def retry_on_exceptions(exception_list = [RestClient::RestHandler], pause: 1,
|
89
|
-
count: 3)
|
90
|
-
define_method('retry_on_exceptions') { exception_list }
|
91
|
-
define_method('retry_pause_time') { pause }
|
92
|
-
define_method('retry_exception_limit') { count }
|
93
|
-
end
|
94
|
-
|
95
|
-
private
|
96
|
-
|
97
|
-
# Load credentials hash from a YAML using Soaspec.credentials_folder if set, adding '.yml' if not set
|
98
|
-
# @return [Hash] Hash with credentials in it
|
99
|
-
def load_credentials_hash(filename)
|
100
|
-
raise ArgumentError, "Filename passed must be a String for #{self} but was #{filename.class}" unless filename.is_a?(String)
|
101
|
-
|
102
|
-
full_path = Soaspec.credentials_folder ? File.join(Soaspec.credentials_folder, filename) : filename
|
103
|
-
full_path += '.yml' unless full_path.end_with?('.yml') # Automatically add 'yml' extension
|
104
|
-
raise "No file at #{full_path}. 'Soaspec.credentials_folder' is '#{Soaspec.credentials_folder}'" unless File.exist? full_path
|
105
|
-
|
106
|
-
file_hash = YAML.load_file(full_path)
|
107
|
-
raise "File at #{full_path} is not a hash" unless file_hash.is_a? Hash
|
108
|
-
|
109
|
-
file_hash.transform_keys_to_symbols
|
110
|
-
end
|
111
|
-
end
|
112
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Soaspec
|
4
|
+
# Methods to define parameters specific to REST handler
|
5
|
+
module RestParameters
|
6
|
+
# Defines method 'base_url_value' containing base URL used in REST requests
|
7
|
+
# @param [String] url Base Url to use in REST requests. Suburl is appended to this
|
8
|
+
def base_url(url)
|
9
|
+
raise ArgumentError, "Base Url passed must be a String for #{self} but was #{url.class}" unless url.is_a?(String)
|
10
|
+
|
11
|
+
define_method('base_url_value') { ERB.new(url).result(binding) }
|
12
|
+
# URL used if subclassing handler that sets this previously
|
13
|
+
define_singleton_method('parent_url') { ERB.new(url).result(binding) }
|
14
|
+
end
|
15
|
+
|
16
|
+
# Will create access_token method based on passed parameters
|
17
|
+
# @param [Hash] params OAuth 2 parameters
|
18
|
+
# @option params [token_url] URL to retrieve OAuth token from. @Note this can be set globally instead of here
|
19
|
+
# @option params [client_id] Client ID
|
20
|
+
# @option params [client_secret] Client Secret
|
21
|
+
# @option params [username] Username used in password grant
|
22
|
+
# @option params [password] Password used in password grant
|
23
|
+
# @option params [security_token] Security Token used in password grant
|
24
|
+
def oauth2(params)
|
25
|
+
# @!method oauth_obj Object to handle oauth2
|
26
|
+
define_method('oauth_obj') { OAuth2.new(params, api_username) }
|
27
|
+
# @!method access_token Retrieve OAuth2 access token
|
28
|
+
define_method('access_token') { oauth_obj.access_token }
|
29
|
+
# @!method instance_url Retrieve instance url from OAuth request
|
30
|
+
define_method('instance_url') { oauth_obj.instance_url }
|
31
|
+
end
|
32
|
+
|
33
|
+
# Pass path to YAML file containing OAuth2 parameters
|
34
|
+
# @param [String] path_to_filename Will have Soaspec.credentials_folder appended to it if set
|
35
|
+
def oauth2_file(path_to_filename)
|
36
|
+
oauth_parameters = load_credentials_hash(path_to_filename)
|
37
|
+
@client_id = oauth_parameters[:client_id] if oauth_parameters[:client_id]
|
38
|
+
oauth2 oauth_parameters
|
39
|
+
end
|
40
|
+
|
41
|
+
# @return [String] Client id obtained from credentials file
|
42
|
+
def client_id
|
43
|
+
raise '@client_id is not set. Set by specifying credentials file with "oauth2_file FILENAME" before this is called' unless @client_id
|
44
|
+
|
45
|
+
@client_id
|
46
|
+
end
|
47
|
+
|
48
|
+
# Define basic authentication
|
49
|
+
# @param [String] user Username to use
|
50
|
+
# @param [String] password Password to use
|
51
|
+
def basic_auth(user: nil, password: nil)
|
52
|
+
raise ArgumentError, "Must pass both 'user' and 'password' for #{self}" unless user && password
|
53
|
+
|
54
|
+
define_method('basic_auth_params') do
|
55
|
+
{ user: user, password: password }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Pass path to YAML file containing Basic Auth parameters (i.e, both username and password)
|
60
|
+
# @param [String] path_to_filename Will have Soaspec.credentials_folder appended to it if set
|
61
|
+
def basic_auth_file(path_to_filename)
|
62
|
+
basic_auth load_credentials_hash(path_to_filename)
|
63
|
+
end
|
64
|
+
|
65
|
+
# @param [Hash] headers Hash of REST headers used in RestClient
|
66
|
+
def headers(headers)
|
67
|
+
define_method('rest_client_headers') { headers }
|
68
|
+
end
|
69
|
+
|
70
|
+
# Convert each key from snake_case to PascalCase
|
71
|
+
def pascal_keys(set)
|
72
|
+
define_method('pascal_keys?') { set }
|
73
|
+
end
|
74
|
+
|
75
|
+
# Pass block to perform after every response is retrieved
|
76
|
+
# @example Throw exception if response body has an 'error' element equal to true
|
77
|
+
# after_response do |response, handler|
|
78
|
+
# raise Soaspec::ResponseError if handler.value_from_path(response, 'error')
|
79
|
+
# end
|
80
|
+
def after_response
|
81
|
+
define_method('after_response') { |response, _self| yield response, self }
|
82
|
+
end
|
83
|
+
|
84
|
+
# @param [Array] exception_list List of exceptions to retry response on. Default is
|
85
|
+
# any REST exception
|
86
|
+
# @param [Integer] pause Time to wait before retrying
|
87
|
+
# @param [Integer] count Times to retry
|
88
|
+
def retry_on_exceptions(exception_list = [RestClient::RestHandler], pause: 1,
|
89
|
+
count: 3)
|
90
|
+
define_method('retry_on_exceptions') { exception_list }
|
91
|
+
define_method('retry_pause_time') { pause }
|
92
|
+
define_method('retry_exception_limit') { count }
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
# Load credentials hash from a YAML using Soaspec.credentials_folder if set, adding '.yml' if not set
|
98
|
+
# @return [Hash] Hash with credentials in it
|
99
|
+
def load_credentials_hash(filename)
|
100
|
+
raise ArgumentError, "Filename passed must be a String for #{self} but was #{filename.class}" unless filename.is_a?(String)
|
101
|
+
|
102
|
+
full_path = Soaspec.credentials_folder ? File.join(Soaspec.credentials_folder, filename) : filename
|
103
|
+
full_path += '.yml' unless full_path.end_with?('.yml') # Automatically add 'yml' extension
|
104
|
+
raise "No file at #{full_path}. 'Soaspec.credentials_folder' is '#{Soaspec.credentials_folder}'" unless File.exist? full_path
|
105
|
+
|
106
|
+
file_hash = YAML.load_file(full_path)
|
107
|
+
raise "File at #{full_path} is not a hash" unless file_hash.is_a? Hash
|
108
|
+
|
109
|
+
file_hash.transform_keys_to_symbols
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -1,42 +1,42 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Soaspec
|
4
|
-
# Defaults for Soaspec RestParameters methods
|
5
|
-
module RestParametersDefaults
|
6
|
-
# This must be overridden by using 'base_url' method within class definition
|
7
|
-
# @return [String] Set through following method. Base URL in REST requests.
|
8
|
-
def base_url_value
|
9
|
-
nil
|
10
|
-
end
|
11
|
-
|
12
|
-
# This returns the base url that can be accessed by a subclass. It is set by
|
13
|
-
# the 'base_url' method
|
14
|
-
#
|
15
|
-
# @example
|
16
|
-
# class Parent < Soaspec::RestHandler
|
17
|
-
# base_url 'parent'
|
18
|
-
# end
|
19
|
-
#
|
20
|
-
# class Child < Parent
|
21
|
-
# base_url "#{parent_url}/child_path"
|
22
|
-
# end
|
23
|
-
#
|
24
|
-
# Child.new.base_url_value # => 'parent/child_path'
|
25
|
-
# @return [String] Base url that can be accessed by a subclass.
|
26
|
-
def parent_url
|
27
|
-
raise 'This needs to be set through base_url method'
|
28
|
-
end
|
29
|
-
|
30
|
-
# @return [Hash] Headers used in RestClient
|
31
|
-
def rest_client_headers
|
32
|
-
{}
|
33
|
-
end
|
34
|
-
|
35
|
-
# Whether to convert each key in the request to PascalCase
|
36
|
-
# It will also auto convert simple XPath, JSONPath where '//' or '..' not specified
|
37
|
-
# @return [Boolean] Whether to convert to PascalCase
|
38
|
-
def pascal_keys?
|
39
|
-
false
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Soaspec
|
4
|
+
# Defaults for Soaspec RestParameters methods
|
5
|
+
module RestParametersDefaults
|
6
|
+
# This must be overridden by using 'base_url' method within class definition
|
7
|
+
# @return [String] Set through following method. Base URL in REST requests.
|
8
|
+
def base_url_value
|
9
|
+
nil
|
10
|
+
end
|
11
|
+
|
12
|
+
# This returns the base url that can be accessed by a subclass. It is set by
|
13
|
+
# the 'base_url' method
|
14
|
+
#
|
15
|
+
# @example
|
16
|
+
# class Parent < Soaspec::RestHandler
|
17
|
+
# base_url 'parent'
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# class Child < Parent
|
21
|
+
# base_url "#{parent_url}/child_path"
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# Child.new.base_url_value # => 'parent/child_path'
|
25
|
+
# @return [String] Base url that can be accessed by a subclass.
|
26
|
+
def parent_url
|
27
|
+
raise 'This needs to be set through base_url method'
|
28
|
+
end
|
29
|
+
|
30
|
+
# @return [Hash] Headers used in RestClient
|
31
|
+
def rest_client_headers
|
32
|
+
{}
|
33
|
+
end
|
34
|
+
|
35
|
+
# Whether to convert each key in the request to PascalCase
|
36
|
+
# It will also auto convert simple XPath, JSONPath where '//' or '..' not specified
|
37
|
+
# @return [Boolean] Whether to convert to PascalCase
|
38
|
+
def pascal_keys?
|
39
|
+
false
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -1,241 +1,241 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'exchange_handler'
|
4
|
-
require_relative '../core_ext/hash'
|
5
|
-
require_relative '../errors'
|
6
|
-
require_relative 'handler_accessors'
|
7
|
-
require_relative '../interpreter'
|
8
|
-
require_relative 'request/soap_request'
|
9
|
-
require 'forwardable'
|
10
|
-
|
11
|
-
module Soaspec
|
12
|
-
# Accessors specific to SOAP handler
|
13
|
-
module SoapAccessors
|
14
|
-
# Define attributes set on root SOAP element
|
15
|
-
# @param [Hash] attributes Attributes used in the root SOAP element
|
16
|
-
def root_attributes(attributes)
|
17
|
-
define_method('request_root_attributes') { attributes }
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
# Wraps around Savon client defining default values dependent on the soap request
|
22
|
-
class SoapHandler < ExchangeHandler
|
23
|
-
extend Soaspec::SoapAccessors
|
24
|
-
extend Forwardable
|
25
|
-
|
26
|
-
delegate [:operations] => :client
|
27
|
-
|
28
|
-
# Savon client used to make SOAP calls
|
29
|
-
attr_accessor :client
|
30
|
-
# SOAP Operation to use by default
|
31
|
-
attr_accessor :operation
|
32
|
-
|
33
|
-
# Attributes set at the root XML element of SOAP request
|
34
|
-
def request_root_attributes; 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
|
-
end
|
69
|
-
|
70
|
-
# Setup object to handle communicating with a particular SOAP WSDL
|
71
|
-
# @param [String] name Name to describe handler. Used in calling 'to_s'
|
72
|
-
# @param [Hash] options Options defining SOAP request. WSDL, authentication, see http://savonrb.com/version2/globals.html for list of options
|
73
|
-
def initialize(name = self.class.to_s, options = {})
|
74
|
-
if name.is_a?(Hash) && options == {} # If name is not set
|
75
|
-
options = name
|
76
|
-
name = self.class.to_s
|
77
|
-
end
|
78
|
-
super
|
79
|
-
set_remove_keys(options, %i[operation default_hash template_name])
|
80
|
-
merged_options = Soaspec::SpecLogger.log_api_traffic? ? default_options.merge(logging_options) : default_options
|
81
|
-
merged_options.merge! savon_options
|
82
|
-
merged_options.merge!(options)
|
83
|
-
self.client = Savon.client(merged_options)
|
84
|
-
end
|
85
|
-
|
86
|
-
# @param [Hash] override_parameters Parameters for building the request
|
87
|
-
# @return [Hash] Parameters used in making a request
|
88
|
-
def request_parameters(override_parameters)
|
89
|
-
SoapRequest.new(operation, request_body_params(override_parameters), @request_option)
|
90
|
-
end
|
91
|
-
|
92
|
-
# Used in making request via hash or in template via Erb
|
93
|
-
# @param [Hash] request_parameters Hash representing elements to send in request
|
94
|
-
# If the :body key is set, this will be used as the request body
|
95
|
-
def request_body_params(request_parameters)
|
96
|
-
test_values = request_parameters[:body] || request_parameters
|
97
|
-
test_values.transform_keys_to_symbols if Soaspec.always_use_keys?
|
98
|
-
if @request_option == :template
|
99
|
-
test_values = IndifferentHash.new(test_values) # Allow test_values to be either Symbol or String
|
100
|
-
{ xml: Soaspec::TemplateReader.new.render_body(template_name, binding) }
|
101
|
-
elsif @request_option == :hash
|
102
|
-
{ message: @default_hash.merge(test_values), attributes: request_root_attributes }
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
# Used in together with Exchange request that passes such override parameters
|
107
|
-
# @param [Hash] request_parameters Parameters used to overwrite defaults in request
|
108
|
-
def make_request(request_parameters)
|
109
|
-
# Call the SOAP operation with the request XML provided
|
110
|
-
request = request_parameters(request_parameters)
|
111
|
-
begin
|
112
|
-
client.call request.operation, request.body # request_body_params(request_parameters)
|
113
|
-
rescue Savon::HTTPError => e
|
114
|
-
soap_error
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
# @param [Hash] format Format of expected result
|
119
|
-
# @return [Object] Generic body to be displayed in error messages
|
120
|
-
def response_body(response, format: :hash)
|
121
|
-
case format
|
122
|
-
when :hash
|
123
|
-
response.body
|
124
|
-
when :raw
|
125
|
-
response.xml
|
126
|
-
else
|
127
|
-
response.body
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
|
-
# @return [Boolean] Whether the request found the desired value or not
|
132
|
-
def found?(response)
|
133
|
-
status_code_for(response) != 404
|
134
|
-
end
|
135
|
-
|
136
|
-
# Response status code for response. '200' indicates a success
|
137
|
-
# @param [Savon::Response] response
|
138
|
-
# @return [Integer] Status code
|
139
|
-
def status_code_for(response)
|
140
|
-
response.http.code
|
141
|
-
end
|
142
|
-
|
143
|
-
# @return [Boolean] Whether response includes provided string within it
|
144
|
-
def include_in_body?(response, expected)
|
145
|
-
response.to_xml.to_s.include? expected
|
146
|
-
end
|
147
|
-
|
148
|
-
# @param [Symbol] expected
|
149
|
-
# @return [Boolean] Whether response body contains expected key
|
150
|
-
def include_key?(response, expected)
|
151
|
-
body = response.body
|
152
|
-
body.extend Hashie::Extensions::DeepFind
|
153
|
-
!body.deep_find_all(expected).empty?
|
154
|
-
end
|
155
|
-
|
156
|
-
# Convert all XML nodes to lowercase
|
157
|
-
# @param [Nokogiri::XML::Document] xml_doc Xml document to convert
|
158
|
-
def convert_to_lower_case(xml_doc)
|
159
|
-
xml_doc.traverse do |node|
|
160
|
-
node.name = node.name.downcase if node.is_a?(Nokogiri::XML::Element)
|
161
|
-
end
|
162
|
-
end
|
163
|
-
|
164
|
-
# Returns the value at the provided xpath
|
165
|
-
# @param [Savon::Response] response
|
166
|
-
# @param [String] xpath
|
167
|
-
# @return [Enumerable] Elements found through Xpath
|
168
|
-
def xpath_elements_for(response: nil, xpath: nil, attribute: nil)
|
169
|
-
raise ArgumentError('response and xpath must be passed to method') unless response && xpath
|
170
|
-
|
171
|
-
xpath = "//*[@#{attribute}]" unless attribute.nil?
|
172
|
-
xpath = '//' + xpath if xpath[0] != '/'
|
173
|
-
temp_doc = response.doc.dup
|
174
|
-
convert_to_lower_case(temp_doc) if convert_to_lower?
|
175
|
-
if strip_namespaces? && !xpath.include?(':')
|
176
|
-
temp_doc.remove_namespaces!
|
177
|
-
temp_doc.xpath(xpath)
|
178
|
-
else
|
179
|
-
temp_doc.xpath(xpath, temp_doc.collect_namespaces)
|
180
|
-
end
|
181
|
-
end
|
182
|
-
|
183
|
-
# Based on a exchange, return the value at the provided xpath
|
184
|
-
# If the path does not begin with a '/', a '//' is added to it
|
185
|
-
# @param [Savon::Response] response
|
186
|
-
# @param [String] path Xpath
|
187
|
-
# @param [String] attribute Generic attribute to find. Will override path
|
188
|
-
# @return [String] Value at Xpath
|
189
|
-
def value_from_path(response, path, attribute: nil)
|
190
|
-
results = xpath_elements_for(response: response, xpath: path, attribute: attribute)
|
191
|
-
raise NoElementAtPath, "No value at Xpath '#{path}' in XML #{response.doc}" if results.empty?
|
192
|
-
return results.first.inner_text if attribute.nil?
|
193
|
-
|
194
|
-
results.first.attributes[attribute].inner_text
|
195
|
-
end
|
196
|
-
|
197
|
-
# @return [Enumerable] List of values returned from path
|
198
|
-
def values_from_path(response, path, attribute: nil)
|
199
|
-
xpath_elements_for(response: response, xpath: path, attribute: attribute).map(&:inner_text)
|
200
|
-
end
|
201
|
-
|
202
|
-
# alias elements xpath_elements_for
|
203
|
-
|
204
|
-
# @return [Boolean] Whether any of the keys of the Body Hash include value
|
205
|
-
def include_value?(response, expected_value)
|
206
|
-
response.body.include_value?(expected_value)
|
207
|
-
end
|
208
|
-
|
209
|
-
# Hash of response body
|
210
|
-
def to_hash(response)
|
211
|
-
response.body
|
212
|
-
end
|
213
|
-
|
214
|
-
# Convenience methods for once off usage of a SOAP request
|
215
|
-
class << self
|
216
|
-
# Implement undefined setter with []= for FactoryBot to use without needing to define params to set
|
217
|
-
# @param [Object] method_name Name of method not defined
|
218
|
-
# @param [Object] args Arguments passed to method
|
219
|
-
# @param [Object] block
|
220
|
-
def method_missing(method_name, *args, &block)
|
221
|
-
tmp_class = new(method_name)
|
222
|
-
operations = tmp_class.operations
|
223
|
-
if operations.include? method_name
|
224
|
-
tmp_class.operation = method_name
|
225
|
-
exchange = Exchange.new(method_name, *args)
|
226
|
-
exchange.exchange_handler = tmp_class
|
227
|
-
yield exchange if block_given?
|
228
|
-
exchange
|
229
|
-
else
|
230
|
-
super
|
231
|
-
end
|
232
|
-
end
|
233
|
-
|
234
|
-
def respond_to_missing?(method_name, *args)
|
235
|
-
tmp_class = new(args)
|
236
|
-
operations = tmp_class.operations
|
237
|
-
operations.include?(method_name) || super
|
238
|
-
end
|
239
|
-
end
|
240
|
-
end
|
241
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'exchange_handler'
|
4
|
+
require_relative '../core_ext/hash'
|
5
|
+
require_relative '../errors'
|
6
|
+
require_relative 'handler_accessors'
|
7
|
+
require_relative '../interpreter'
|
8
|
+
require_relative 'request/soap_request'
|
9
|
+
require 'forwardable'
|
10
|
+
|
11
|
+
module Soaspec
|
12
|
+
# Accessors specific to SOAP handler
|
13
|
+
module SoapAccessors
|
14
|
+
# Define attributes set on root SOAP element
|
15
|
+
# @param [Hash] attributes Attributes used in the root SOAP element
|
16
|
+
def root_attributes(attributes)
|
17
|
+
define_method('request_root_attributes') { attributes }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Wraps around Savon client defining default values dependent on the soap request
|
22
|
+
class SoapHandler < ExchangeHandler
|
23
|
+
extend Soaspec::SoapAccessors
|
24
|
+
extend Forwardable
|
25
|
+
|
26
|
+
delegate [:operations] => :client
|
27
|
+
|
28
|
+
# Savon client used to make SOAP calls
|
29
|
+
attr_accessor :client
|
30
|
+
# SOAP Operation to use by default
|
31
|
+
attr_accessor :operation
|
32
|
+
|
33
|
+
# Attributes set at the root XML element of SOAP request
|
34
|
+
def request_root_attributes; 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
|
+
end
|
69
|
+
|
70
|
+
# Setup object to handle communicating with a particular SOAP WSDL
|
71
|
+
# @param [String] name Name to describe handler. Used in calling 'to_s'
|
72
|
+
# @param [Hash] options Options defining SOAP request. WSDL, authentication, see http://savonrb.com/version2/globals.html for list of options
|
73
|
+
def initialize(name = self.class.to_s, options = {})
|
74
|
+
if name.is_a?(Hash) && options == {} # If name is not set
|
75
|
+
options = name
|
76
|
+
name = self.class.to_s
|
77
|
+
end
|
78
|
+
super
|
79
|
+
set_remove_keys(options, %i[operation default_hash template_name])
|
80
|
+
merged_options = Soaspec::SpecLogger.log_api_traffic? ? default_options.merge(logging_options) : default_options
|
81
|
+
merged_options.merge! savon_options
|
82
|
+
merged_options.merge!(options)
|
83
|
+
self.client = Savon.client(merged_options)
|
84
|
+
end
|
85
|
+
|
86
|
+
# @param [Hash] override_parameters Parameters for building the request
|
87
|
+
# @return [Hash] Parameters used in making a request
|
88
|
+
def request_parameters(override_parameters)
|
89
|
+
SoapRequest.new(operation, request_body_params(override_parameters), @request_option)
|
90
|
+
end
|
91
|
+
|
92
|
+
# Used in making request via hash or in template via Erb
|
93
|
+
# @param [Hash] request_parameters Hash representing elements to send in request
|
94
|
+
# If the :body key is set, this will be used as the request body
|
95
|
+
def request_body_params(request_parameters)
|
96
|
+
test_values = request_parameters[:body] || request_parameters
|
97
|
+
test_values.transform_keys_to_symbols if Soaspec.always_use_keys?
|
98
|
+
if @request_option == :template
|
99
|
+
test_values = IndifferentHash.new(test_values) # Allow test_values to be either Symbol or String
|
100
|
+
{ xml: Soaspec::TemplateReader.new.render_body(template_name, binding) }
|
101
|
+
elsif @request_option == :hash
|
102
|
+
{ message: @default_hash.merge(test_values), attributes: request_root_attributes }
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# Used in together with Exchange request that passes such override parameters
|
107
|
+
# @param [Hash] request_parameters Parameters used to overwrite defaults in request
|
108
|
+
def make_request(request_parameters)
|
109
|
+
# Call the SOAP operation with the request XML provided
|
110
|
+
request = request_parameters(request_parameters)
|
111
|
+
begin
|
112
|
+
client.call request.operation, request.body # request_body_params(request_parameters)
|
113
|
+
rescue Savon::HTTPError => e
|
114
|
+
soap_error
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# @param [Hash] format Format of expected result
|
119
|
+
# @return [Object] Generic body to be displayed in error messages
|
120
|
+
def response_body(response, format: :hash)
|
121
|
+
case format
|
122
|
+
when :hash
|
123
|
+
response.body
|
124
|
+
when :raw
|
125
|
+
response.xml
|
126
|
+
else
|
127
|
+
response.body
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# @return [Boolean] Whether the request found the desired value or not
|
132
|
+
def found?(response)
|
133
|
+
status_code_for(response) != 404
|
134
|
+
end
|
135
|
+
|
136
|
+
# Response status code for response. '200' indicates a success
|
137
|
+
# @param [Savon::Response] response
|
138
|
+
# @return [Integer] Status code
|
139
|
+
def status_code_for(response)
|
140
|
+
response.http.code
|
141
|
+
end
|
142
|
+
|
143
|
+
# @return [Boolean] Whether response includes provided string within it
|
144
|
+
def include_in_body?(response, expected)
|
145
|
+
response.to_xml.to_s.include? expected
|
146
|
+
end
|
147
|
+
|
148
|
+
# @param [Symbol] expected
|
149
|
+
# @return [Boolean] Whether response body contains expected key
|
150
|
+
def include_key?(response, expected)
|
151
|
+
body = response.body
|
152
|
+
body.extend Hashie::Extensions::DeepFind
|
153
|
+
!body.deep_find_all(expected).empty?
|
154
|
+
end
|
155
|
+
|
156
|
+
# Convert all XML nodes to lowercase
|
157
|
+
# @param [Nokogiri::XML::Document] xml_doc Xml document to convert
|
158
|
+
def convert_to_lower_case(xml_doc)
|
159
|
+
xml_doc.traverse do |node|
|
160
|
+
node.name = node.name.downcase if node.is_a?(Nokogiri::XML::Element)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
# Returns the value at the provided xpath
|
165
|
+
# @param [Savon::Response] response
|
166
|
+
# @param [String] xpath
|
167
|
+
# @return [Enumerable] Elements found through Xpath
|
168
|
+
def xpath_elements_for(response: nil, xpath: nil, attribute: nil)
|
169
|
+
raise ArgumentError('response and xpath must be passed to method') unless response && xpath
|
170
|
+
|
171
|
+
xpath = "//*[@#{attribute}]" unless attribute.nil?
|
172
|
+
xpath = '//' + xpath if xpath[0] != '/'
|
173
|
+
temp_doc = response.doc.dup
|
174
|
+
convert_to_lower_case(temp_doc) if convert_to_lower?
|
175
|
+
if strip_namespaces? && !xpath.include?(':')
|
176
|
+
temp_doc.remove_namespaces!
|
177
|
+
temp_doc.xpath(xpath)
|
178
|
+
else
|
179
|
+
temp_doc.xpath(xpath, temp_doc.collect_namespaces)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
# Based on a exchange, return the value at the provided xpath
|
184
|
+
# If the path does not begin with a '/', a '//' is added to it
|
185
|
+
# @param [Savon::Response] response
|
186
|
+
# @param [String] path Xpath
|
187
|
+
# @param [String] attribute Generic attribute to find. Will override path
|
188
|
+
# @return [String] Value at Xpath
|
189
|
+
def value_from_path(response, path, attribute: nil)
|
190
|
+
results = xpath_elements_for(response: response, xpath: path, attribute: attribute)
|
191
|
+
raise NoElementAtPath, "No value at Xpath '#{path}' in XML #{response.doc}" if results.empty?
|
192
|
+
return results.first.inner_text if attribute.nil?
|
193
|
+
|
194
|
+
results.first.attributes[attribute].inner_text
|
195
|
+
end
|
196
|
+
|
197
|
+
# @return [Enumerable] List of values returned from path
|
198
|
+
def values_from_path(response, path, attribute: nil)
|
199
|
+
xpath_elements_for(response: response, xpath: path, attribute: attribute).map(&:inner_text)
|
200
|
+
end
|
201
|
+
|
202
|
+
# alias elements xpath_elements_for
|
203
|
+
|
204
|
+
# @return [Boolean] Whether any of the keys of the Body Hash include value
|
205
|
+
def include_value?(response, expected_value)
|
206
|
+
response.body.include_value?(expected_value)
|
207
|
+
end
|
208
|
+
|
209
|
+
# Hash of response body
|
210
|
+
def to_hash(response)
|
211
|
+
response.body
|
212
|
+
end
|
213
|
+
|
214
|
+
# Convenience methods for once off usage of a SOAP request
|
215
|
+
class << self
|
216
|
+
# Implement undefined setter with []= for FactoryBot to use without needing to define params to set
|
217
|
+
# @param [Object] method_name Name of method not defined
|
218
|
+
# @param [Object] args Arguments passed to method
|
219
|
+
# @param [Object] block
|
220
|
+
def method_missing(method_name, *args, &block)
|
221
|
+
tmp_class = new(method_name)
|
222
|
+
operations = tmp_class.operations
|
223
|
+
if operations.include? method_name
|
224
|
+
tmp_class.operation = method_name
|
225
|
+
exchange = Exchange.new(method_name, *args)
|
226
|
+
exchange.exchange_handler = tmp_class
|
227
|
+
yield exchange if block_given?
|
228
|
+
exchange
|
229
|
+
else
|
230
|
+
super
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
def respond_to_missing?(method_name, *args)
|
235
|
+
tmp_class = new(args)
|
236
|
+
operations = tmp_class.operations
|
237
|
+
operations.include?(method_name) || super
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|