soaspec 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +15 -15
  3. data/.gitlab-ci.yml +31 -31
  4. data/.rspec +3 -3
  5. data/.rubocop.yml +2 -2
  6. data/CODE_OF_CONDUCT.md +74 -74
  7. data/ChangeLog +384 -384
  8. data/Gemfile +6 -6
  9. data/LICENSE.txt +21 -21
  10. data/README.md +85 -85
  11. data/Rakefile +24 -24
  12. data/Todo.md +6 -6
  13. data/exe/soaspec +119 -119
  14. data/exe/soaspec-virtual-server +103 -103
  15. data/exe/xml_to_yaml_file +60 -60
  16. data/lib/soaspec.rb +91 -91
  17. data/lib/soaspec/core_ext/hash.rb +83 -83
  18. data/lib/soaspec/exchange.rb +234 -234
  19. data/lib/soaspec/exchange_handlers/exchange_handler.rb +103 -103
  20. data/lib/soaspec/exchange_handlers/handler_accessors.rb +106 -106
  21. data/lib/soaspec/exchange_handlers/rest_accessors.rb +92 -92
  22. data/lib/soaspec/exchange_handlers/rest_handler.rb +311 -311
  23. data/lib/soaspec/exchange_handlers/rest_methods.rb +44 -44
  24. data/lib/soaspec/exchange_handlers/soap_handler.rb +236 -236
  25. data/lib/soaspec/exe_helpers.rb +56 -56
  26. data/lib/soaspec/generator/.rspec.erb +5 -5
  27. data/lib/soaspec/generator/.travis.yml.erb +5 -5
  28. data/lib/soaspec/generator/Gemfile.erb +8 -8
  29. data/lib/soaspec/generator/README.md.erb +29 -29
  30. data/lib/soaspec/generator/Rakefile.erb +19 -19
  31. data/lib/soaspec/generator/config/data/default.yml.erb +1 -1
  32. data/lib/soaspec/generator/lib/blz_service.rb.erb +26 -26
  33. data/lib/soaspec/generator/lib/dynamic_class_content.rb.erb +12 -12
  34. data/lib/soaspec/generator/lib/shared_example.rb.erb +8 -8
  35. data/lib/soaspec/generator/spec/dynamic_soap_spec.rb.erb +12 -12
  36. data/lib/soaspec/generator/spec/soap_spec.rb.erb +51 -51
  37. data/lib/soaspec/generator/spec/spec_helper.rb.erb +20 -20
  38. data/lib/soaspec/generator/template/soap_template.xml +6 -6
  39. data/lib/soaspec/interpreter.rb +40 -40
  40. data/lib/soaspec/matchers.rb +65 -65
  41. data/lib/soaspec/not_found_errors.rb +13 -13
  42. data/lib/soaspec/soaspec_shared_examples.rb +24 -24
  43. data/lib/soaspec/spec_logger.rb +27 -27
  44. data/lib/soaspec/test_server/bank.wsdl +90 -90
  45. data/lib/soaspec/test_server/get_bank.rb +160 -160
  46. data/lib/soaspec/test_server/invoices.rb +27 -27
  47. data/lib/soaspec/test_server/namespace.xml +14 -14
  48. data/lib/soaspec/test_server/note.xml +5 -5
  49. data/lib/soaspec/test_server/puppy_service.rb +20 -20
  50. data/lib/soaspec/test_server/test_attribute.rb +13 -13
  51. data/lib/soaspec/version.rb +2 -2
  52. data/lib/soaspec/wsdl_generator.rb +144 -144
  53. data/soaspec.gemspec +46 -45
  54. data/test.wsdl +116 -116
  55. data/test.xml +10 -10
  56. data/test_wsdl.rb +43 -43
  57. metadata +17 -3
@@ -1,83 +1,83 @@
1
-
2
-
3
-
4
- # Override Hash class with convience methods
5
- class Hash
6
-
7
- # Transform each key in Hash to a symbol. Privately used by non-self method
8
- def self.transform_keys_to_symbols(value)
9
- return value if not value.is_a?(Hash)
10
- hash = value.inject({}){|memo,(k,v)| memo[k.to_sym] = Hash.transform_keys_to_symbols(v); memo}
11
- return hash
12
- end
13
-
14
- # Take keys of hash and transform those to a symbols
15
- def transform_keys_to_symbols
16
- inject({}){|memo, (k, v)| memo[k.to_sym] = Hash.transform_keys_to_symbols(v); memo}
17
- end
18
-
19
- # Returns all keys that have a particular value within a nested Hash
20
- def find_all_values_for(key)
21
- result = []
22
- result << self[key]
23
- self.values.each do |hash_value|
24
- # next if hash_value.is_a? Array
25
- #
26
- if hash_value.is_a?(Array)
27
- hash_value.each do |array_element|
28
- result += array_element.find_all_values_for(key) if array_element.is_a? Hash
29
- end
30
- end
31
-
32
- values = [hash_value]
33
- values.each do |value|
34
- result += value.find_all_values_for(key) if value.is_a? Hash
35
- end
36
- end
37
- result.compact
38
- end
39
-
40
- # Value present in nested Hash.
41
- def include_value?(value)
42
- each_value do |v|
43
- return true if v == value
44
- next unless v.is_a? Hash
45
- v.each_value do |v|
46
- return true if v == value
47
- next unless v.is_a? Hash
48
- v.each_value do |v|
49
- return true if v == value
50
- end
51
- end
52
- end
53
- false
54
- end
55
-
56
- # # Whether key is present at least once
57
- # def include_key?(key)
58
- # result = find_all_values_for key
59
- # result != []
60
- # end
61
-
62
- # Loop through each item within a key within a Hash if the key exists
63
- # @param [Key] key Key within hash to iterate through
64
- def each_if_not_null(key)
65
- case key.class.to_s
66
- when 'String'
67
- if self[key]
68
- self[key].each do |list_item|
69
- yield(list_item)
70
- end
71
- end
72
- when 'Array', 'Hash'
73
- if self[key[0]]
74
- if self[key[0]][key[1]]
75
- self[key[0]][key[1]].each do |list_item|
76
- yield(list_item)
77
- end
78
- end
79
- end
80
- end
81
- end
82
-
83
- end
1
+
2
+
3
+
4
+ # Override Hash class with convience methods
5
+ class Hash
6
+
7
+ # Transform each key in Hash to a symbol. Privately used by non-self method
8
+ def self.transform_keys_to_symbols(value)
9
+ return value if not value.is_a?(Hash)
10
+ hash = value.inject({}){|memo,(k,v)| memo[k.to_sym] = Hash.transform_keys_to_symbols(v); memo}
11
+ return hash
12
+ end
13
+
14
+ # Take keys of hash and transform those to a symbols
15
+ def transform_keys_to_symbols
16
+ inject({}){|memo, (k, v)| memo[k.to_sym] = Hash.transform_keys_to_symbols(v); memo}
17
+ end
18
+
19
+ # Returns all keys that have a particular value within a nested Hash
20
+ def find_all_values_for(key)
21
+ result = []
22
+ result << self[key]
23
+ self.values.each do |hash_value|
24
+ # next if hash_value.is_a? Array
25
+ #
26
+ if hash_value.is_a?(Array)
27
+ hash_value.each do |array_element|
28
+ result += array_element.find_all_values_for(key) if array_element.is_a? Hash
29
+ end
30
+ end
31
+
32
+ values = [hash_value]
33
+ values.each do |value|
34
+ result += value.find_all_values_for(key) if value.is_a? Hash
35
+ end
36
+ end
37
+ result.compact
38
+ end
39
+
40
+ # Value present in nested Hash.
41
+ def include_value?(value)
42
+ each_value do |v|
43
+ return true if v == value
44
+ next unless v.is_a? Hash
45
+ v.each_value do |v|
46
+ return true if v == value
47
+ next unless v.is_a? Hash
48
+ v.each_value do |v|
49
+ return true if v == value
50
+ end
51
+ end
52
+ end
53
+ false
54
+ end
55
+
56
+ # # Whether key is present at least once
57
+ # def include_key?(key)
58
+ # result = find_all_values_for key
59
+ # result != []
60
+ # end
61
+
62
+ # Loop through each item within a key within a Hash if the key exists
63
+ # @param [Key] key Key within hash to iterate through
64
+ def each_if_not_null(key)
65
+ case key.class.to_s
66
+ when 'String'
67
+ if self[key]
68
+ self[key].each do |list_item|
69
+ yield(list_item)
70
+ end
71
+ end
72
+ when 'Array', 'Hash'
73
+ if self[key[0]]
74
+ if self[key[0]][key[1]]
75
+ self[key[0]][key[1]].each do |list_item|
76
+ yield(list_item)
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+
83
+ end
@@ -1,235 +1,235 @@
1
- require_relative '../soaspec'
2
-
3
- # Convenience methods to set Exchange specific properties
4
- module ExchangeAccessors
5
-
6
- # Set default exchange handler for this exchange
7
- # This is helpful for when you need a new exchange handler created for each exchange
8
- def default_handler(handler_class, name = handler_class.to_s, params = '')
9
- define_method('default_handler_used') do
10
- params_used = Hash[params.map do |k, param|
11
- [k, param.is_a?(String) ? ERB.new(param).result(binding) : param]
12
- end]
13
- handler_class.new name, params_used
14
- end
15
- end
16
-
17
- # Set retry_for_success to true, retrying response until a successful status code is returned
18
- def expect_positive_status(retry_count: 3)
19
- define_method('retry_count') do
20
- retry_count
21
- end
22
- define_method('retry_for_success?') do
23
- true
24
- end
25
- end
26
-
27
- end
28
-
29
- # This represents a request / response pair
30
- # Essentially, params in the exchange that are set are related to the request
31
- # What is returned is related to the response
32
- class Exchange
33
- extend ExchangeAccessors
34
-
35
- # Instance of ExchangeHandler for which this exchange is made
36
- attr_accessor :exchange_handler
37
- # How many times to retry for a success
38
- attr_accessor :retry_count
39
- # Name used for displaying class
40
- attr_accessor :test_name
41
- # Expect Factory to fail upon trying to create
42
- attr_writer :fail_factory
43
-
44
-
45
- def values_from_path(path, attribute: nil)
46
- exchange_handler.values_from_path(response, path, attribute: attribute)
47
- end
48
-
49
- # Set retry for success variable to true so that request will be retried
50
- # for retry_count until it's true
51
- def retry_for_success
52
- @retry_for_success = true
53
- self
54
- end
55
-
56
- # @return [Bool] Whether to keep making request until success code reached
57
- def retry_for_success?
58
- @retry_for_success
59
- end
60
-
61
- # @return [Boolean] Soaspec::ExchangeHandler used by this exchange
62
- def default_handler_used
63
- nil
64
- end
65
-
66
- # @param [String] element Element to define methods for
67
- def methods_for_element(element)
68
- element_name = element.to_s.split('__custom_path_').last
69
- define_singleton_method(element_name) do
70
- exchange_handler.__send__(element, response) # Forward the call onto handler to retrieve the element for the response
71
- end
72
- define_singleton_method("#{element_name}?") do
73
- begin
74
- __send__ element_name
75
- true
76
- rescue NoElementAtPath
77
- false
78
- end
79
- end
80
- end
81
-
82
- # @param [Symbol, String] name Name shown in RSpec run
83
- # @param [Hash] override_parameters Parameters to override for default params
84
- def initialize(name = self.class.to_s, override_parameters = {})
85
- self.test_name ||= name.to_s
86
- # As a last resort this uses the global parameter. The handler should be set straight before an exchange is made to use this
87
- @exchange_handler ||= default_handler_used || Soaspec.api_handler
88
- raise '@exchange_handler not set. Set either with `Soaspec.api_handler = Handler.new` or within the exchange' unless @exchange_handler
89
- @fail_factory = nil
90
- @override_parameters = override_parameters
91
- @retry_for_success = false
92
- self.retry_count = 3
93
- @exchange_handler.elements.each { |element| methods_for_element(element) }
94
- end
95
-
96
- # Specify a url to add onto the base_url of the ExchangeHandler used
97
- # @param [String] url Url to add onto the base_url of the ExchangeHandler used
98
- def suburl=(url)
99
- @override_parameters[:suburl] = url
100
- end
101
-
102
- # Specify HTTP method to use. Default is :post
103
- # @param [Symbol] method HTTP method. E.g, :get, :patch
104
- def method=(method)
105
- @override_parameters[:method] = method
106
- end
107
-
108
- # Make request to handler with parameters defined
109
- # Will retry until success code reached if retry_for_success? is set
110
- # @return [Response] Response from Api handler
111
- def make_request
112
- Soaspec::SpecLogger.info 'Example ' + test_name
113
- request_params = @override_parameters
114
- (1..retry_count).each do |count|
115
- response = exchange_handler.make_request(request_params)
116
- return response unless retry_for_success?
117
- return response if (200..299).cover? @exchange_handler.status_code_for(response)
118
- sleep 0.5
119
- break response if count == retry_count
120
- end
121
- end
122
-
123
- # Stores a value in the api handler that can be accessed by the provided name
124
- # @param [Symbol] name Name of method to use to access this value within handler
125
- # @param [String] value Path to value to store
126
- def store(name, value)
127
- exchange_handler.store(name, self[value])
128
- end
129
-
130
- # Retrieve the stored value from the Api Handler
131
- # @param [String, Symbol] name Name of value to retrieve
132
- # @return [Object] value from the Api Handler stored previously
133
- def retrieve(name)
134
- method = '__stored_val__' + name.to_s
135
- raise ArgumentError('Value not stored at ') unless exchange_handler.respond_to? method
136
- exchange_handler.send(method)
137
- end
138
-
139
- # Name describing this class when used with `RSpec.describe`
140
- # This will make the request and store the response
141
- # @return [String] Name given when initializing
142
- def to_s
143
- test_name
144
- end
145
-
146
- # Returns response object from Api. Will make the request if not made and then cache it for later on
147
- # For example for SOAP it will be a Savon response
148
- # response.body (body of response as Hash)
149
- # response.header (head of response as Hash)
150
- def response
151
- @response ||= make_request
152
- end
153
-
154
- alias call response
155
-
156
- # Get status code from api class. This is http response for Web Api
157
- # @return [Integer] Status code from api class
158
- def status_code
159
- exchange_handler.status_code_for(response)
160
- end
161
-
162
- # Dummy request used to make a request without verifying it and ignoring WSDL errors
163
- # @return [Boolean] Always returns true. Unless of course an unexpected exception occurs
164
- def dummy_request
165
- make_request
166
- true
167
- rescue Savon::HTTPError
168
- puts 'Resolver error'
169
- # This seems to occur first time IP address asks for WSDL
170
- true
171
- end
172
-
173
- # @return [Boolean] Whether an element exists at the path
174
- def element?(path)
175
- [path]
176
- true
177
- rescue NoElementAtPath
178
- false
179
- end
180
-
181
- # Extract value from path api class
182
- # @param [Object] path Path to return element for api class E.g - for SOAP this is XPath string. For JSON, this is Hash dig Array
183
- # @return [String] Value at path
184
- def [](path)
185
- exchange_handler.value_from_path(response, path.to_s)
186
- end
187
-
188
- # Set a parameter request in the request body.
189
- # Can be used to build a request over several steps (e.g Cucumber)
190
- # Will be used with FactoryBot
191
- def []=(key, value)
192
- @override_parameters[:body] ||= {}
193
- @override_parameters[:body][key] = value
194
- end
195
-
196
- # Implement undefined setter with []= for FactoryBot to use without needing to define params to set
197
- # @param [Object] method_name Name of method not defined
198
- # @param [Object] args Arguments passed to method
199
- # @param [Object] block
200
- def method_missing(method_name, *args, &block)
201
- set_value = args.first
202
- if method_name[-1] == '=' # A setter method
203
- getter_name = method_name[0..-2]
204
- if set_value.class < Exchange # This would be prerequisite exchange
205
- define_singleton_method(getter_name) do
206
- set_value
207
- end
208
- self[getter_name] = set_value.id if set_value.respond_to?(:id)
209
- else
210
- self[getter_name] = set_value
211
- end
212
- else
213
- super
214
- end
215
- end
216
-
217
- # Used for setters that are not defined
218
- def respond_to_missing?(method_name, *args)
219
- method_name[-1] == '=' || super
220
- end
221
-
222
- # Makes request, caching the response and returning self
223
- # Used by FactoryBot
224
- # @return [Self]
225
- def save!
226
- @retry_for_success = @fail_factory ? false : true
227
- call
228
- self
229
- end
230
-
231
- def to_hash
232
- exchange_handler.to_hash(response)
233
- end
234
-
1
+ require_relative '../soaspec'
2
+
3
+ # Convenience methods to set Exchange specific properties
4
+ module ExchangeAccessors
5
+
6
+ # Set default exchange handler for this exchange
7
+ # This is helpful for when you need a new exchange handler created for each exchange
8
+ def default_handler(handler_class, name = handler_class.to_s, params = '')
9
+ define_method('default_handler_used') do
10
+ params_used = Hash[params.map do |k, param|
11
+ [k, param.is_a?(String) ? ERB.new(param).result(binding) : param]
12
+ end]
13
+ handler_class.new name, params_used
14
+ end
15
+ end
16
+
17
+ # Set retry_for_success to true, retrying response until a successful status code is returned
18
+ def expect_positive_status(retry_count: 3)
19
+ define_method('retry_count') do
20
+ retry_count
21
+ end
22
+ define_method('retry_for_success?') do
23
+ true
24
+ end
25
+ end
26
+
27
+ end
28
+
29
+ # This represents a request / response pair
30
+ # Essentially, params in the exchange that are set are related to the request
31
+ # What is returned is related to the response
32
+ class Exchange
33
+ extend ExchangeAccessors
34
+
35
+ # Instance of ExchangeHandler for which this exchange is made
36
+ attr_accessor :exchange_handler
37
+ # How many times to retry for a success
38
+ attr_accessor :retry_count
39
+ # Name used for displaying class
40
+ attr_accessor :test_name
41
+ # Expect Factory to fail upon trying to create
42
+ attr_writer :fail_factory
43
+
44
+
45
+ def values_from_path(path, attribute: nil)
46
+ exchange_handler.values_from_path(response, path, attribute: attribute)
47
+ end
48
+
49
+ # Set retry for success variable to true so that request will be retried
50
+ # for retry_count until it's true
51
+ def retry_for_success
52
+ @retry_for_success = true
53
+ self
54
+ end
55
+
56
+ # @return [Bool] Whether to keep making request until success code reached
57
+ def retry_for_success?
58
+ @retry_for_success
59
+ end
60
+
61
+ # @return [Boolean] Soaspec::ExchangeHandler used by this exchange
62
+ def default_handler_used
63
+ nil
64
+ end
65
+
66
+ # @param [String] element Element to define methods for
67
+ def methods_for_element(element)
68
+ element_name = element.to_s.split('__custom_path_').last
69
+ define_singleton_method(element_name) do
70
+ exchange_handler.__send__(element, response) # Forward the call onto handler to retrieve the element for the response
71
+ end
72
+ define_singleton_method("#{element_name}?") do
73
+ begin
74
+ __send__ element_name
75
+ true
76
+ rescue NoElementAtPath
77
+ false
78
+ end
79
+ end
80
+ end
81
+
82
+ # @param [Symbol, String] name Name shown in RSpec run
83
+ # @param [Hash] override_parameters Parameters to override for default params
84
+ def initialize(name = self.class.to_s, override_parameters = {})
85
+ self.test_name ||= name.to_s
86
+ # As a last resort this uses the global parameter. The handler should be set straight before an exchange is made to use this
87
+ @exchange_handler ||= default_handler_used || Soaspec.api_handler
88
+ raise '@exchange_handler not set. Set either with `Soaspec.api_handler = Handler.new` or within the exchange' unless @exchange_handler
89
+ @fail_factory = nil
90
+ @override_parameters = override_parameters
91
+ @retry_for_success = false
92
+ self.retry_count = 3
93
+ @exchange_handler.elements.each { |element| methods_for_element(element) }
94
+ end
95
+
96
+ # Specify a url to add onto the base_url of the ExchangeHandler used
97
+ # @param [String] url Url to add onto the base_url of the ExchangeHandler used
98
+ def suburl=(url)
99
+ @override_parameters[:suburl] = url
100
+ end
101
+
102
+ # Specify HTTP method to use. Default is :post
103
+ # @param [Symbol] method HTTP method. E.g, :get, :patch
104
+ def method=(method)
105
+ @override_parameters[:method] = method
106
+ end
107
+
108
+ # Make request to handler with parameters defined
109
+ # Will retry until success code reached if retry_for_success? is set
110
+ # @return [Response] Response from Api handler
111
+ def make_request
112
+ Soaspec::SpecLogger.info 'Example ' + test_name
113
+ request_params = @override_parameters
114
+ (1..retry_count).each do |count|
115
+ response = exchange_handler.make_request(request_params)
116
+ return response unless retry_for_success?
117
+ return response if (200..299).cover? @exchange_handler.status_code_for(response)
118
+ sleep 0.5
119
+ break response if count == retry_count
120
+ end
121
+ end
122
+
123
+ # Stores a value in the api handler that can be accessed by the provided name
124
+ # @param [Symbol] name Name of method to use to access this value within handler
125
+ # @param [String] value Path to value to store
126
+ def store(name, value)
127
+ exchange_handler.store(name, self[value])
128
+ end
129
+
130
+ # Retrieve the stored value from the Api Handler
131
+ # @param [String, Symbol] name Name of value to retrieve
132
+ # @return [Object] value from the Api Handler stored previously
133
+ def retrieve(name)
134
+ method = '__stored_val__' + name.to_s
135
+ raise ArgumentError('Value not stored at ') unless exchange_handler.respond_to? method
136
+ exchange_handler.send(method)
137
+ end
138
+
139
+ # Name describing this class when used with `RSpec.describe`
140
+ # This will make the request and store the response
141
+ # @return [String] Name given when initializing
142
+ def to_s
143
+ test_name
144
+ end
145
+
146
+ # Returns response object from Api. Will make the request if not made and then cache it for later on
147
+ # For example for SOAP it will be a Savon response
148
+ # response.body (body of response as Hash)
149
+ # response.header (head of response as Hash)
150
+ def response
151
+ @response ||= make_request
152
+ end
153
+
154
+ alias call response
155
+
156
+ # Get status code from api class. This is http response for Web Api
157
+ # @return [Integer] Status code from api class
158
+ def status_code
159
+ exchange_handler.status_code_for(response)
160
+ end
161
+
162
+ # Dummy request used to make a request without verifying it and ignoring WSDL errors
163
+ # @return [Boolean] Always returns true. Unless of course an unexpected exception occurs
164
+ def dummy_request
165
+ make_request
166
+ true
167
+ rescue Savon::HTTPError
168
+ puts 'Resolver error'
169
+ # This seems to occur first time IP address asks for WSDL
170
+ true
171
+ end
172
+
173
+ # @return [Boolean] Whether an element exists at the path
174
+ def element?(path)
175
+ [path]
176
+ true
177
+ rescue NoElementAtPath
178
+ false
179
+ end
180
+
181
+ # Extract value from path api class
182
+ # @param [Object] path Path to return element for api class E.g - for SOAP this is XPath string. For JSON, this is Hash dig Array
183
+ # @return [String] Value at path
184
+ def [](path)
185
+ exchange_handler.value_from_path(response, path.to_s)
186
+ end
187
+
188
+ # Set a parameter request in the request body.
189
+ # Can be used to build a request over several steps (e.g Cucumber)
190
+ # Will be used with FactoryBot
191
+ def []=(key, value)
192
+ @override_parameters[:body] ||= {}
193
+ @override_parameters[:body][key] = value
194
+ end
195
+
196
+ # Implement undefined setter with []= for FactoryBot to use without needing to define params to set
197
+ # @param [Object] method_name Name of method not defined
198
+ # @param [Object] args Arguments passed to method
199
+ # @param [Object] block
200
+ def method_missing(method_name, *args, &block)
201
+ set_value = args.first
202
+ if method_name[-1] == '=' # A setter method
203
+ getter_name = method_name[0..-2]
204
+ if set_value.class < Exchange # This would be prerequisite exchange
205
+ define_singleton_method(getter_name) do
206
+ set_value
207
+ end
208
+ self[getter_name] = set_value.id if set_value.respond_to?(:id)
209
+ else
210
+ self[getter_name] = set_value
211
+ end
212
+ else
213
+ super
214
+ end
215
+ end
216
+
217
+ # Used for setters that are not defined
218
+ def respond_to_missing?(method_name, *args)
219
+ method_name[-1] == '=' || super
220
+ end
221
+
222
+ # Makes request, caching the response and returning self
223
+ # Used by FactoryBot
224
+ # @return [Self]
225
+ def save!
226
+ @retry_for_success = @fail_factory ? false : true
227
+ call
228
+ self
229
+ end
230
+
231
+ def to_hash
232
+ exchange_handler.to_hash(response)
233
+ end
234
+
235
235
  end