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,259 +1,259 @@
|
|
1
|
-
require_relative 'exchange_handler'
|
2
|
-
require_relative 'rest_parameters'
|
3
|
-
require_relative 'rest_parameters_defaults'
|
4
|
-
require_relative 'rest_exchanger_factory'
|
5
|
-
require_relative '../core_ext/hash'
|
6
|
-
require_relative '../not_found_errors'
|
7
|
-
require_relative 'handler_accessors'
|
8
|
-
require_relative '../interpreter'
|
9
|
-
require_relative 'response_extractor'
|
10
|
-
require 'json'
|
11
|
-
require 'jsonpath'
|
12
|
-
require 'nori'
|
13
|
-
require 'erb'
|
14
|
-
|
15
|
-
module Soaspec
|
16
|
-
# Wraps around Savon client defining default values dependent on the soap request
|
17
|
-
class RestHandler < ExchangeHandler
|
18
|
-
include ResponseExtractor
|
19
|
-
extend Soaspec::RestParameters
|
20
|
-
include Soaspec::RestParametersDefaults
|
21
|
-
extend Soaspec::RestExchangeFactory
|
22
|
-
|
23
|
-
# User used in making API calls
|
24
|
-
attr_accessor :api_username
|
25
|
-
|
26
|
-
# Setup object to handle communicating with a particular SOAP WSDL
|
27
|
-
# @param [Hash] options Options defining REST request. base_url, default_hash
|
28
|
-
def initialize(name = self.class.to_s, options = {})
|
29
|
-
raise "Base URL not set! Please set in class with 'base_url' method" unless base_url_value
|
30
|
-
|
31
|
-
if name.is_a?(Hash) && options == {} # If name is not set, use first parameter as the options hash
|
32
|
-
options = name
|
33
|
-
name = self.class.to_s
|
34
|
-
end
|
35
|
-
super
|
36
|
-
set_remove_keys(options, %i[api_username default_hash template_name])
|
37
|
-
@init_options = options
|
38
|
-
init_merge_options # Call this to verify any issues with options on creating object
|
39
|
-
end
|
40
|
-
|
41
|
-
# Used in together with Exchange request that passes such override parameters
|
42
|
-
# @param [Hash] override_parameters Params to characterize REST request
|
43
|
-
# @option override_parameters [Hash] :params Extra parameters (E.g. headers)
|
44
|
-
# @option override_parameters [String] suburl URL appended to base_url of class
|
45
|
-
# @option override_parameters [Hash] :q Query for REST
|
46
|
-
# @option override_parameters [Symbol] :method REST method (:get, :post, :patch, etc)
|
47
|
-
# Following are for the body of the request
|
48
|
-
# @option override_parameters [Hash] :body Hash to be converted to JSON in request body
|
49
|
-
# @option override_parameters [String] :payload String to be passed directly in request body
|
50
|
-
# @option override_parameters [String] :template_name Path to file to be read via ERB and passed in request body
|
51
|
-
# @return [RestClient::Response] Response from making request
|
52
|
-
def make_request(override_parameters)
|
53
|
-
@merged_options ||= init_merge_options
|
54
|
-
test_values = override_parameters
|
55
|
-
test_values[:params] ||= {}
|
56
|
-
test_values[:method] ||= :post
|
57
|
-
test_values[:suburl] = test_values[:suburl].to_s if test_values[:suburl]
|
58
|
-
test_values[:params][:params] = test_values[:q] if test_values[:q] # Use q for query parameters. Nested :params is ugly and long
|
59
|
-
# In order for ERB to be calculated at correct time, the first time request is made, the resource should be created
|
60
|
-
@resource ||= RestClient::Resource.new(ERB.new(base_url_value).result(binding), @merged_options)
|
61
|
-
|
62
|
-
@resource_used = test_values[:suburl] ? @resource[test_values[:suburl]] : @resource
|
63
|
-
|
64
|
-
begin
|
65
|
-
response = case test_values[:method]
|
66
|
-
when :post, :patch, :put
|
67
|
-
Soaspec::SpecLogger.info("request body: #{post_data(test_values)}")
|
68
|
-
@resource_used.send(test_values[:method].to_s, post_data(test_values), test_values[:params])
|
69
|
-
else # :get, :delete
|
70
|
-
@resource_used.send(test_values[:method].to_s, test_values[:params])
|
71
|
-
end
|
72
|
-
rescue RestClient::ExceptionWithResponse => e
|
73
|
-
response = e.response
|
74
|
-
end
|
75
|
-
Soaspec::SpecLogger.info(["response_headers: #{response.headers}", "response_body: #{response}"])
|
76
|
-
response
|
77
|
-
end
|
78
|
-
|
79
|
-
# Add values to here when extending this class to have default REST options.
|
80
|
-
# See rest client resource at https://github.com/rest-client/rest-client for details
|
81
|
-
# It's easier to set headers via 'headers' accessor rather than here
|
82
|
-
# @return [Hash] Options adding to & overriding defaults
|
83
|
-
def rest_resource_options
|
84
|
-
{
|
85
|
-
}
|
86
|
-
end
|
87
|
-
|
88
|
-
# Perform ERB on each header value
|
89
|
-
# @return [Hash] Hash from 'rest_client_headers' passed through ERB
|
90
|
-
def parse_headers
|
91
|
-
Hash[rest_client_headers.map do |header_name, header_value|
|
92
|
-
raise ArgumentError, "Header '#{header_name}' is null. Headers are #{rest_client_headers}" if header_value.nil?
|
93
|
-
|
94
|
-
[header_name, ERB.new(header_value).result(binding)]
|
95
|
-
end]
|
96
|
-
end
|
97
|
-
|
98
|
-
# Initialize value of merged options
|
99
|
-
# @return [Hash] Hash of merged options
|
100
|
-
def init_merge_options
|
101
|
-
options = rest_resource_options
|
102
|
-
options.merge! basic_auth_params if respond_to? :basic_auth_params
|
103
|
-
options[:headers] ||= {}
|
104
|
-
options[:headers].merge! parse_headers
|
105
|
-
options[:headers][:authorization] ||= ERB.new('Bearer <%= access_token %>').result(binding) if Soaspec.auto_oauth && respond_to?(:access_token)
|
106
|
-
options.merge(@init_options)
|
107
|
-
end
|
108
|
-
|
109
|
-
# @param [Hash] format Format of expected result.
|
110
|
-
# @return [Object] Generic body to be displayed in error messages
|
111
|
-
def response_body(response, format: :hash)
|
112
|
-
extract_hash response
|
113
|
-
end
|
114
|
-
|
115
|
-
# @return [Boolean] Whether response body includes String
|
116
|
-
def include_in_body?(response, expected)
|
117
|
-
response.body.include? expected
|
118
|
-
end
|
119
|
-
|
120
|
-
# @@return [Boolean] Whether the request found the desired value or not
|
121
|
-
def found?(response)
|
122
|
-
status_code_for(response) != 404
|
123
|
-
end
|
124
|
-
|
125
|
-
# @return [Boolean] Whether response contains expected value
|
126
|
-
def include_value?(response, expected)
|
127
|
-
extract_hash(response).include_value? expected
|
128
|
-
end
|
129
|
-
|
130
|
-
# @return [Boolean] Whether response body contains expected key
|
131
|
-
def include_key?(response, expected)
|
132
|
-
value_from_path(response, expected)
|
133
|
-
end
|
134
|
-
|
135
|
-
# @return [Integer] HTTP Status code for response
|
136
|
-
def status_code_for(response)
|
137
|
-
response.code
|
138
|
-
end
|
139
|
-
|
140
|
-
# Returns the value at the provided xpath
|
141
|
-
# @param [RestClient::Response] response
|
142
|
-
# @param [String] xpath Path to find elements from
|
143
|
-
# @param [String] attribute Attribute to find path for
|
144
|
-
# @return [Enumerable] Value inside element found through Xpath
|
145
|
-
def xpath_elements_for(response: nil, xpath: nil, attribute: nil)
|
146
|
-
raise ArgumentError unless response && xpath
|
147
|
-
raise "Can't perform XPATH if response is not XML" unless Interpreter.response_type_for(response) == :xml
|
148
|
-
|
149
|
-
xpath = prefix_xpath(xpath, attribute)
|
150
|
-
temp_doc = Nokogiri.parse(response.body).dup
|
151
|
-
if strip_namespaces? && !xpath.include?(':')
|
152
|
-
temp_doc.remove_namespaces!
|
153
|
-
temp_doc.xpath(xpath)
|
154
|
-
else
|
155
|
-
temp_doc.xpath(xpath, temp_doc.collect_namespaces)
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
# @return [Enumerable] List of values matching JSON path
|
160
|
-
def json_path_values_for(response, path, attribute: nil)
|
161
|
-
raise 'JSON does not support attributes' if attribute
|
162
|
-
|
163
|
-
JsonPath.on(response.body, path)
|
164
|
-
end
|
165
|
-
|
166
|
-
# Calculate all JSON path values based on rules. ',', pascal_case
|
167
|
-
# @param [RestClient::Response] response Response from API
|
168
|
-
# @param [Object] path Xpath, JSONPath or other path identifying how to find element
|
169
|
-
# @param [String] attribute Generic attribute to find. Will override path
|
170
|
-
# @param [Boolean] not_empty Whether to fail if result is empty
|
171
|
-
# @return [Array] Paths to check as first and matching values (List of values matching JSON Path) as second
|
172
|
-
def calculated_json_path_matches(path, response, attribute, not_empty: false)
|
173
|
-
path = add_pascal_path(path)
|
174
|
-
paths_to_check = path.split(',')
|
175
|
-
paths_to_check = paths_to_check.map { |path_to_check| prefix_json_path(path_to_check) }
|
176
|
-
matching_values = paths_to_check.collect do |path_to_check|
|
177
|
-
json_path_values_for(response, path_to_check, attribute: attribute)
|
178
|
-
end.reject(&:empty?)
|
179
|
-
raise NoElementAtPath, "No value at JSONPath '#{paths_to_check}' in '#{response.body}'" if matching_values.empty? && not_empty
|
180
|
-
|
181
|
-
matching_values.first
|
182
|
-
end
|
183
|
-
|
184
|
-
# Based on a exchange, return the value at the provided xpath
|
185
|
-
# If the path does not begin with a '/', a '//' is added to it
|
186
|
-
# @param [RestClient::Response] response Response from API
|
187
|
-
# @param [Object] path Xpath, JSONPath or other path identifying how to find element
|
188
|
-
# @param [String] attribute Generic attribute to find. Will override path
|
189
|
-
# @return [String] Value at Xpath
|
190
|
-
def value_from_path(response, path, attribute: nil)
|
191
|
-
path = path.to_s
|
192
|
-
case Interpreter.response_type_for(response)
|
193
|
-
when :xml
|
194
|
-
result = xpath_elements_for(response: response, xpath: path, attribute: attribute).first
|
195
|
-
raise NoElementAtPath, "No value at Xpath '#{prefix_xpath(path, attribute)}' in '#{response.body}'" unless result
|
196
|
-
return result.inner_text if attribute.nil?
|
197
|
-
|
198
|
-
return result.attributes[attribute].inner_text
|
199
|
-
when :json
|
200
|
-
matching_values = calculated_json_path_matches(path, response, attribute, not_empty: true)
|
201
|
-
matching_values.first
|
202
|
-
else # Assume this is a String
|
203
|
-
raise NoElementAtPath, 'Response is empty' if response.to_s.empty?
|
204
|
-
|
205
|
-
response.to_s[/#{path}/] # Perform regular expression using path if not XML nor JSON
|
206
|
-
end
|
207
|
-
end
|
208
|
-
|
209
|
-
# @return [Enumerable] List of values returned from path
|
210
|
-
def values_from_path(response, path, attribute: nil)
|
211
|
-
path = path.to_s
|
212
|
-
case Interpreter.response_type_for(response)
|
213
|
-
when :xml
|
214
|
-
xpath_elements_for(response: response, xpath: path, attribute: attribute).map(&:inner_text)
|
215
|
-
when :json
|
216
|
-
result = calculated_json_path_matches(path, response, attribute)
|
217
|
-
result || []
|
218
|
-
# json_path_values_for(response, path, attribute: attribute)
|
219
|
-
else
|
220
|
-
raise "Unable to interpret type of #{response.body}"
|
221
|
-
end
|
222
|
-
end
|
223
|
-
|
224
|
-
# @return [RestClient::Request] Request of API call. Either intended request or actual request
|
225
|
-
def request(response)
|
226
|
-
return 'Request not yet sent' if response.nil?
|
227
|
-
|
228
|
-
response.request
|
229
|
-
end
|
230
|
-
|
231
|
-
private
|
232
|
-
|
233
|
-
# Work out data to send based upon payload, template_name, or body
|
234
|
-
# @return [String] Payload to send in REST request
|
235
|
-
def post_data(test_values)
|
236
|
-
data = if @request_option == :hash && !test_values[:payload]
|
237
|
-
test_values[:payload] = JSON.generate(hash_used_in_request(test_values[:body])).to_s
|
238
|
-
elsif @request_option == :template
|
239
|
-
test_values = test_values[:body].dup if test_values[:body]
|
240
|
-
test_values = IndifferentHash.new(test_values) # Allow test_values to be either Symbol or String
|
241
|
-
Soaspec::TemplateReader.new.render_body(template_name, binding)
|
242
|
-
else
|
243
|
-
test_values[:payload]
|
244
|
-
end
|
245
|
-
# Soaspec::SpecLogger.info "Request Empty for '#{@request_option}'" if data.strip.empty?
|
246
|
-
data
|
247
|
-
end
|
248
|
-
|
249
|
-
# @return [Hash] Hash used in REST request based on data conversion
|
250
|
-
def hash_used_in_request(override_hash)
|
251
|
-
request = override_hash ? @default_hash.merge(override_hash) : @default_hash
|
252
|
-
if pascal_keys?
|
253
|
-
request.map { |k, v| [convert_to_pascal_case(k.to_s), v] }.to_h
|
254
|
-
else
|
255
|
-
request
|
256
|
-
end
|
257
|
-
end
|
258
|
-
end
|
259
|
-
end
|
1
|
+
require_relative 'exchange_handler'
|
2
|
+
require_relative 'rest_parameters'
|
3
|
+
require_relative 'rest_parameters_defaults'
|
4
|
+
require_relative 'rest_exchanger_factory'
|
5
|
+
require_relative '../core_ext/hash'
|
6
|
+
require_relative '../not_found_errors'
|
7
|
+
require_relative 'handler_accessors'
|
8
|
+
require_relative '../interpreter'
|
9
|
+
require_relative 'response_extractor'
|
10
|
+
require 'json'
|
11
|
+
require 'jsonpath'
|
12
|
+
require 'nori'
|
13
|
+
require 'erb'
|
14
|
+
|
15
|
+
module Soaspec
|
16
|
+
# Wraps around Savon client defining default values dependent on the soap request
|
17
|
+
class RestHandler < ExchangeHandler
|
18
|
+
include ResponseExtractor
|
19
|
+
extend Soaspec::RestParameters
|
20
|
+
include Soaspec::RestParametersDefaults
|
21
|
+
extend Soaspec::RestExchangeFactory
|
22
|
+
|
23
|
+
# User used in making API calls
|
24
|
+
attr_accessor :api_username
|
25
|
+
|
26
|
+
# Setup object to handle communicating with a particular SOAP WSDL
|
27
|
+
# @param [Hash] options Options defining REST request. base_url, default_hash
|
28
|
+
def initialize(name = self.class.to_s, options = {})
|
29
|
+
raise "Base URL not set! Please set in class with 'base_url' method" unless base_url_value
|
30
|
+
|
31
|
+
if name.is_a?(Hash) && options == {} # If name is not set, use first parameter as the options hash
|
32
|
+
options = name
|
33
|
+
name = self.class.to_s
|
34
|
+
end
|
35
|
+
super
|
36
|
+
set_remove_keys(options, %i[api_username default_hash template_name])
|
37
|
+
@init_options = options
|
38
|
+
init_merge_options # Call this to verify any issues with options on creating object
|
39
|
+
end
|
40
|
+
|
41
|
+
# Used in together with Exchange request that passes such override parameters
|
42
|
+
# @param [Hash] override_parameters Params to characterize REST request
|
43
|
+
# @option override_parameters [Hash] :params Extra parameters (E.g. headers)
|
44
|
+
# @option override_parameters [String] suburl URL appended to base_url of class
|
45
|
+
# @option override_parameters [Hash] :q Query for REST
|
46
|
+
# @option override_parameters [Symbol] :method REST method (:get, :post, :patch, etc)
|
47
|
+
# Following are for the body of the request
|
48
|
+
# @option override_parameters [Hash] :body Hash to be converted to JSON in request body
|
49
|
+
# @option override_parameters [String] :payload String to be passed directly in request body
|
50
|
+
# @option override_parameters [String] :template_name Path to file to be read via ERB and passed in request body
|
51
|
+
# @return [RestClient::Response] Response from making request
|
52
|
+
def make_request(override_parameters)
|
53
|
+
@merged_options ||= init_merge_options
|
54
|
+
test_values = override_parameters
|
55
|
+
test_values[:params] ||= {}
|
56
|
+
test_values[:method] ||= :post
|
57
|
+
test_values[:suburl] = test_values[:suburl].to_s if test_values[:suburl]
|
58
|
+
test_values[:params][:params] = test_values[:q] if test_values[:q] # Use q for query parameters. Nested :params is ugly and long
|
59
|
+
# In order for ERB to be calculated at correct time, the first time request is made, the resource should be created
|
60
|
+
@resource ||= RestClient::Resource.new(ERB.new(base_url_value).result(binding), @merged_options)
|
61
|
+
|
62
|
+
@resource_used = test_values[:suburl] ? @resource[test_values[:suburl]] : @resource
|
63
|
+
|
64
|
+
begin
|
65
|
+
response = case test_values[:method]
|
66
|
+
when :post, :patch, :put
|
67
|
+
Soaspec::SpecLogger.info("request body: #{post_data(test_values)}")
|
68
|
+
@resource_used.send(test_values[:method].to_s, post_data(test_values), test_values[:params])
|
69
|
+
else # :get, :delete
|
70
|
+
@resource_used.send(test_values[:method].to_s, test_values[:params])
|
71
|
+
end
|
72
|
+
rescue RestClient::ExceptionWithResponse => e
|
73
|
+
response = e.response
|
74
|
+
end
|
75
|
+
Soaspec::SpecLogger.info(["response_headers: #{response.headers}", "response_body: #{response}"])
|
76
|
+
response
|
77
|
+
end
|
78
|
+
|
79
|
+
# Add values to here when extending this class to have default REST options.
|
80
|
+
# See rest client resource at https://github.com/rest-client/rest-client for details
|
81
|
+
# It's easier to set headers via 'headers' accessor rather than here
|
82
|
+
# @return [Hash] Options adding to & overriding defaults
|
83
|
+
def rest_resource_options
|
84
|
+
{
|
85
|
+
}
|
86
|
+
end
|
87
|
+
|
88
|
+
# Perform ERB on each header value
|
89
|
+
# @return [Hash] Hash from 'rest_client_headers' passed through ERB
|
90
|
+
def parse_headers
|
91
|
+
Hash[rest_client_headers.map do |header_name, header_value|
|
92
|
+
raise ArgumentError, "Header '#{header_name}' is null. Headers are #{rest_client_headers}" if header_value.nil?
|
93
|
+
|
94
|
+
[header_name, ERB.new(header_value).result(binding)]
|
95
|
+
end]
|
96
|
+
end
|
97
|
+
|
98
|
+
# Initialize value of merged options
|
99
|
+
# @return [Hash] Hash of merged options
|
100
|
+
def init_merge_options
|
101
|
+
options = rest_resource_options
|
102
|
+
options.merge! basic_auth_params if respond_to? :basic_auth_params
|
103
|
+
options[:headers] ||= {}
|
104
|
+
options[:headers].merge! parse_headers
|
105
|
+
options[:headers][:authorization] ||= ERB.new('Bearer <%= access_token %>').result(binding) if Soaspec.auto_oauth && respond_to?(:access_token)
|
106
|
+
options.merge(@init_options)
|
107
|
+
end
|
108
|
+
|
109
|
+
# @param [Hash] format Format of expected result.
|
110
|
+
# @return [Object] Generic body to be displayed in error messages
|
111
|
+
def response_body(response, format: :hash)
|
112
|
+
extract_hash response
|
113
|
+
end
|
114
|
+
|
115
|
+
# @return [Boolean] Whether response body includes String
|
116
|
+
def include_in_body?(response, expected)
|
117
|
+
response.body.include? expected
|
118
|
+
end
|
119
|
+
|
120
|
+
# @@return [Boolean] Whether the request found the desired value or not
|
121
|
+
def found?(response)
|
122
|
+
status_code_for(response) != 404
|
123
|
+
end
|
124
|
+
|
125
|
+
# @return [Boolean] Whether response contains expected value
|
126
|
+
def include_value?(response, expected)
|
127
|
+
extract_hash(response).include_value? expected
|
128
|
+
end
|
129
|
+
|
130
|
+
# @return [Boolean] Whether response body contains expected key
|
131
|
+
def include_key?(response, expected)
|
132
|
+
value_from_path(response, expected)
|
133
|
+
end
|
134
|
+
|
135
|
+
# @return [Integer] HTTP Status code for response
|
136
|
+
def status_code_for(response)
|
137
|
+
response.code
|
138
|
+
end
|
139
|
+
|
140
|
+
# Returns the value at the provided xpath
|
141
|
+
# @param [RestClient::Response] response
|
142
|
+
# @param [String] xpath Path to find elements from
|
143
|
+
# @param [String] attribute Attribute to find path for
|
144
|
+
# @return [Enumerable] Value inside element found through Xpath
|
145
|
+
def xpath_elements_for(response: nil, xpath: nil, attribute: nil)
|
146
|
+
raise ArgumentError unless response && xpath
|
147
|
+
raise "Can't perform XPATH if response is not XML" unless Interpreter.response_type_for(response) == :xml
|
148
|
+
|
149
|
+
xpath = prefix_xpath(xpath, attribute)
|
150
|
+
temp_doc = Nokogiri.parse(response.body).dup
|
151
|
+
if strip_namespaces? && !xpath.include?(':')
|
152
|
+
temp_doc.remove_namespaces!
|
153
|
+
temp_doc.xpath(xpath)
|
154
|
+
else
|
155
|
+
temp_doc.xpath(xpath, temp_doc.collect_namespaces)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
# @return [Enumerable] List of values matching JSON path
|
160
|
+
def json_path_values_for(response, path, attribute: nil)
|
161
|
+
raise 'JSON does not support attributes' if attribute
|
162
|
+
|
163
|
+
JsonPath.on(response.body, path)
|
164
|
+
end
|
165
|
+
|
166
|
+
# Calculate all JSON path values based on rules. ',', pascal_case
|
167
|
+
# @param [RestClient::Response] response Response from API
|
168
|
+
# @param [Object] path Xpath, JSONPath or other path identifying how to find element
|
169
|
+
# @param [String] attribute Generic attribute to find. Will override path
|
170
|
+
# @param [Boolean] not_empty Whether to fail if result is empty
|
171
|
+
# @return [Array] Paths to check as first and matching values (List of values matching JSON Path) as second
|
172
|
+
def calculated_json_path_matches(path, response, attribute, not_empty: false)
|
173
|
+
path = add_pascal_path(path)
|
174
|
+
paths_to_check = path.split(',')
|
175
|
+
paths_to_check = paths_to_check.map { |path_to_check| prefix_json_path(path_to_check) }
|
176
|
+
matching_values = paths_to_check.collect do |path_to_check|
|
177
|
+
json_path_values_for(response, path_to_check, attribute: attribute)
|
178
|
+
end.reject(&:empty?)
|
179
|
+
raise NoElementAtPath, "No value at JSONPath '#{paths_to_check}' in '#{response.body}'" if matching_values.empty? && not_empty
|
180
|
+
|
181
|
+
matching_values.first
|
182
|
+
end
|
183
|
+
|
184
|
+
# Based on a exchange, return the value at the provided xpath
|
185
|
+
# If the path does not begin with a '/', a '//' is added to it
|
186
|
+
# @param [RestClient::Response] response Response from API
|
187
|
+
# @param [Object] path Xpath, JSONPath or other path identifying how to find element
|
188
|
+
# @param [String] attribute Generic attribute to find. Will override path
|
189
|
+
# @return [String] Value at Xpath
|
190
|
+
def value_from_path(response, path, attribute: nil)
|
191
|
+
path = path.to_s
|
192
|
+
case Interpreter.response_type_for(response)
|
193
|
+
when :xml
|
194
|
+
result = xpath_elements_for(response: response, xpath: path, attribute: attribute).first
|
195
|
+
raise NoElementAtPath, "No value at Xpath '#{prefix_xpath(path, attribute)}' in '#{response.body}'" unless result
|
196
|
+
return result.inner_text if attribute.nil?
|
197
|
+
|
198
|
+
return result.attributes[attribute].inner_text
|
199
|
+
when :json
|
200
|
+
matching_values = calculated_json_path_matches(path, response, attribute, not_empty: true)
|
201
|
+
matching_values.first
|
202
|
+
else # Assume this is a String
|
203
|
+
raise NoElementAtPath, 'Response is empty' if response.to_s.empty?
|
204
|
+
|
205
|
+
response.to_s[/#{path}/] # Perform regular expression using path if not XML nor JSON
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
# @return [Enumerable] List of values returned from path
|
210
|
+
def values_from_path(response, path, attribute: nil)
|
211
|
+
path = path.to_s
|
212
|
+
case Interpreter.response_type_for(response)
|
213
|
+
when :xml
|
214
|
+
xpath_elements_for(response: response, xpath: path, attribute: attribute).map(&:inner_text)
|
215
|
+
when :json
|
216
|
+
result = calculated_json_path_matches(path, response, attribute)
|
217
|
+
result || []
|
218
|
+
# json_path_values_for(response, path, attribute: attribute)
|
219
|
+
else
|
220
|
+
raise "Unable to interpret type of #{response.body}"
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
# @return [RestClient::Request] Request of API call. Either intended request or actual request
|
225
|
+
def request(response)
|
226
|
+
return 'Request not yet sent' if response.nil?
|
227
|
+
|
228
|
+
response.request
|
229
|
+
end
|
230
|
+
|
231
|
+
private
|
232
|
+
|
233
|
+
# Work out data to send based upon payload, template_name, or body
|
234
|
+
# @return [String] Payload to send in REST request
|
235
|
+
def post_data(test_values)
|
236
|
+
data = if @request_option == :hash && !test_values[:payload]
|
237
|
+
test_values[:payload] = JSON.generate(hash_used_in_request(test_values[:body])).to_s
|
238
|
+
elsif @request_option == :template
|
239
|
+
test_values = test_values[:body].dup if test_values[:body]
|
240
|
+
test_values = IndifferentHash.new(test_values) # Allow test_values to be either Symbol or String
|
241
|
+
Soaspec::TemplateReader.new.render_body(template_name, binding)
|
242
|
+
else
|
243
|
+
test_values[:payload]
|
244
|
+
end
|
245
|
+
# Soaspec::SpecLogger.info "Request Empty for '#{@request_option}'" if data.strip.empty?
|
246
|
+
data
|
247
|
+
end
|
248
|
+
|
249
|
+
# @return [Hash] Hash used in REST request based on data conversion
|
250
|
+
def hash_used_in_request(override_hash)
|
251
|
+
request = override_hash ? @default_hash.merge(override_hash) : @default_hash
|
252
|
+
if pascal_keys?
|
253
|
+
request.map { |k, v| [convert_to_pascal_case(k.to_s), v] }.to_h
|
254
|
+
else
|
255
|
+
request
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|