lib/soaspec/exchange.rb in soaspec-0.0.81 vs lib/soaspec/exchange.rb in soaspec-0.0.82

- old
+ new

@@ -1,231 +1,231 @@ -require_relative '../soaspec' - -# Convenience methods to set Exchange specific properties -module ExchangeAccessors - - # Set default exchange handler for this exchange - # This is helpful for when you need a new exchange handler created for each exchange - def default_handler(handler_class, name = handler_class.to_s, params = '') - define_method('default_handler_used') do - params_used = Hash[params.map do |k, param| - [k, param.is_a?(String) ? ERB.new(param).result(binding) : param] - end] - handler_class.new name, params_used - end - end - - # Set retry_for_success to true, retrying response until a successful status code is returned - def expect_positive_status(retry_count: 3) - define_method('retry_count') do - retry_count - end - define_method('retry_for_success?') do - true - end - end - -end - -# This represents a request / response pair -# Essentially, params in the exchange that are set are related to the request -# What is returned is related to the response -class Exchange - extend ExchangeAccessors - - # Instance of ExchangeHandler for which this exchange is made - attr_accessor :exchange_handler - # How many times to retry for a success - attr_accessor :retry_count - # Name used for displaying class - attr_accessor :test_name - # Expect Factory to fail upon trying to create - attr_writer :fail_factory - - - def values_from_path(path, attribute: nil) - exchange_handler.values_from_path(response, path, attribute: attribute) - end - - # Set retry for success variable to true so that request will be retried - # for retry_count until it's true - def retry_for_success - @retry_for_success = true - self - end - - # @return [Bool] Whether to keep making request until success code reached - def retry_for_success? - @retry_for_success - end - - # @return [Boolean] Soaspec::ExchangeHandler used by this exchange - def default_handler_used - nil - end - - # @param [String] element Element to define methods for - def methods_for_element(element) - element_name = element.to_s.split('__custom_path_').last - define_singleton_method(element_name) do - exchange_handler.__send__(element, response) # Forward the call onto handler to retrieve the element for the response - end - define_singleton_method("#{element_name}?") do - begin - __send__ element_name - true - rescue NoElementAtPath - false - end - end - end - - # @param [Symbol, String] name Name shown in RSpec run - # @param [Hash] override_parameters Parameters to override for default params - def initialize(name = self.class.to_s, override_parameters = {}) - self.test_name ||= name.to_s - # As a last resort this uses the global parameter. The handler should be set straight before an exchange is made to use this - @exchange_handler ||= default_handler_used || Soaspec.api_handler - raise '@exchange_handler not set. Set either with `Soaspec.api_handler = Handler.new` or within the exchange' unless @exchange_handler - @fail_factory = nil - @override_parameters = override_parameters - @retry_for_success = false - self.retry_count = 3 - @exchange_handler.elements.each { |element| methods_for_element(element) } - end - - # Specify a url to add onto the base_url of the ExchangeHandler used - # @param [String] url Url to add onto the base_url of the ExchangeHandler used - def suburl=(url) - @override_parameters[:suburl] = url - end - - # Specify HTTP method to use. Default is :post - # @param [Symbol] method HTTP method. E.g, :get, :patch - def method=(method) - @override_parameters[:method] = method - end - - # Make request to handler with parameters defined - # Will retry until success code reached if retry_for_success? is set - # @return [Response] Response from Api handler - def make_request - Soaspec::SpecLogger.add_to 'Example ' + test_name - request_params = @override_parameters - (1..retry_count).each do |count| - response = exchange_handler.make_request(request_params) - return response unless retry_for_success? - return response if (200..299).cover? @exchange_handler.status_code_for(response) - sleep 0.5 - break response if count == retry_count - end - end - - # Stores a value in the api handler that can be accessed by the provided name - # @param [Symbol] name Name of method to use to access this value within handler - # @param [String] value Path to value to store - def store(name, value) - exchange_handler.store(name, self[value]) - end - - # Retrieve the stored value from the Api Handler - # @param [String, Symbol] name Name of value to retrieve - # @return [Object] value from the Api Handler stored previously - def retrieve(name) - method = '__stored_val__' + name.to_s - raise ArgumentError('Value not stored at ') unless exchange_handler.respond_to? method - exchange_handler.send(method) - end - - # Name describing this class when used with `RSpec.describe` - # This will make the request and store the response - # @return [String] Name given when initializing - def to_s - test_name - end - - # Returns response object from Api. Will make the request if not made and then cache it for later on - # For example for SOAP it will be a Savon response - # response.body (body of response as Hash) - # response.header (head of response as Hash) - def response - @response ||= make_request - end - - alias call response - - # Get status code from api class. This is http response for Web Api - # @return [Integer] Status code from api class - def status_code - exchange_handler.status_code_for(response) - end - - # Dummy request used to make a request without verifying it and ignoring WSDL errors - # @return [Boolean] Always returns true. Unless of course an unexpected exception occurs - def dummy_request - make_request - true - rescue Savon::HTTPError - puts 'Resolver error' - # This seems to occur first time IP address asks for WSDL - true - end - - # @return [Boolean] Whether an element exists at the path - def element?(path) - [path] - true - rescue NoElementAtPath - false - end - - # Extract value from path api class - # @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 - # @return [String] Value at path - def [](path) - exchange_handler.value_from_path(response, path.to_s) - end - - # Set a parameter request in the request body. - # Can be used to build a request over several steps (e.g Cucumber) - # Will be used with FactoryBot - def []=(key, value) - @override_parameters[:body] ||= {} - @override_parameters[:body][key] = value - end - - # Implement undefined setter with []= for FactoryBot to use without needing to define params to set - # @param [Object] method_name Name of method not defined - # @param [Object] args Arguments passed to method - # @param [Object] block - def method_missing(method_name, *args, &block) - set_value = args.first - if method_name[-1] == '=' # A setter method - getter_name = method_name[0..-2] - if set_value.class < Exchange # This would be prerequisite exchange - define_singleton_method(getter_name) do - set_value - end - self[getter_name] = set_value.id if set_value.respond_to?(:id) - else - self[getter_name] = set_value - end - else - super - end - end - - # Used for setters that are not defined - def respond_to_missing?(method_name, *args) - method_name[-1] == '=' || super - end - - # Makes request, caching the response and returning self - # Used by FactoryBot - # @return [Self] - def save! - @retry_for_success = @fail_factory ? false : true - call - self - end - +require_relative '../soaspec' + +# Convenience methods to set Exchange specific properties +module ExchangeAccessors + + # Set default exchange handler for this exchange + # This is helpful for when you need a new exchange handler created for each exchange + def default_handler(handler_class, name = handler_class.to_s, params = '') + define_method('default_handler_used') do + params_used = Hash[params.map do |k, param| + [k, param.is_a?(String) ? ERB.new(param).result(binding) : param] + end] + handler_class.new name, params_used + end + end + + # Set retry_for_success to true, retrying response until a successful status code is returned + def expect_positive_status(retry_count: 3) + define_method('retry_count') do + retry_count + end + define_method('retry_for_success?') do + true + end + end + +end + +# This represents a request / response pair +# Essentially, params in the exchange that are set are related to the request +# What is returned is related to the response +class Exchange + extend ExchangeAccessors + + # Instance of ExchangeHandler for which this exchange is made + attr_accessor :exchange_handler + # How many times to retry for a success + attr_accessor :retry_count + # Name used for displaying class + attr_accessor :test_name + # Expect Factory to fail upon trying to create + attr_writer :fail_factory + + + def values_from_path(path, attribute: nil) + exchange_handler.values_from_path(response, path, attribute: attribute) + end + + # Set retry for success variable to true so that request will be retried + # for retry_count until it's true + def retry_for_success + @retry_for_success = true + self + end + + # @return [Bool] Whether to keep making request until success code reached + def retry_for_success? + @retry_for_success + end + + # @return [Boolean] Soaspec::ExchangeHandler used by this exchange + def default_handler_used + nil + end + + # @param [String] element Element to define methods for + def methods_for_element(element) + element_name = element.to_s.split('__custom_path_').last + define_singleton_method(element_name) do + exchange_handler.__send__(element, response) # Forward the call onto handler to retrieve the element for the response + end + define_singleton_method("#{element_name}?") do + begin + __send__ element_name + true + rescue NoElementAtPath + false + end + end + end + + # @param [Symbol, String] name Name shown in RSpec run + # @param [Hash] override_parameters Parameters to override for default params + def initialize(name = self.class.to_s, override_parameters = {}) + self.test_name ||= name.to_s + # As a last resort this uses the global parameter. The handler should be set straight before an exchange is made to use this + @exchange_handler ||= default_handler_used || Soaspec.api_handler + raise '@exchange_handler not set. Set either with `Soaspec.api_handler = Handler.new` or within the exchange' unless @exchange_handler + @fail_factory = nil + @override_parameters = override_parameters + @retry_for_success = false + self.retry_count = 3 + @exchange_handler.elements.each { |element| methods_for_element(element) } + end + + # Specify a url to add onto the base_url of the ExchangeHandler used + # @param [String] url Url to add onto the base_url of the ExchangeHandler used + def suburl=(url) + @override_parameters[:suburl] = url + end + + # Specify HTTP method to use. Default is :post + # @param [Symbol] method HTTP method. E.g, :get, :patch + def method=(method) + @override_parameters[:method] = method + end + + # Make request to handler with parameters defined + # Will retry until success code reached if retry_for_success? is set + # @return [Response] Response from Api handler + def make_request + Soaspec::SpecLogger.add_to 'Example ' + test_name + request_params = @override_parameters + (1..retry_count).each do |count| + response = exchange_handler.make_request(request_params) + return response unless retry_for_success? + return response if (200..299).cover? @exchange_handler.status_code_for(response) + sleep 0.5 + break response if count == retry_count + end + end + + # Stores a value in the api handler that can be accessed by the provided name + # @param [Symbol] name Name of method to use to access this value within handler + # @param [String] value Path to value to store + def store(name, value) + exchange_handler.store(name, self[value]) + end + + # Retrieve the stored value from the Api Handler + # @param [String, Symbol] name Name of value to retrieve + # @return [Object] value from the Api Handler stored previously + def retrieve(name) + method = '__stored_val__' + name.to_s + raise ArgumentError('Value not stored at ') unless exchange_handler.respond_to? method + exchange_handler.send(method) + end + + # Name describing this class when used with `RSpec.describe` + # This will make the request and store the response + # @return [String] Name given when initializing + def to_s + test_name + end + + # Returns response object from Api. Will make the request if not made and then cache it for later on + # For example for SOAP it will be a Savon response + # response.body (body of response as Hash) + # response.header (head of response as Hash) + def response + @response ||= make_request + end + + alias call response + + # Get status code from api class. This is http response for Web Api + # @return [Integer] Status code from api class + def status_code + exchange_handler.status_code_for(response) + end + + # Dummy request used to make a request without verifying it and ignoring WSDL errors + # @return [Boolean] Always returns true. Unless of course an unexpected exception occurs + def dummy_request + make_request + true + rescue Savon::HTTPError + puts 'Resolver error' + # This seems to occur first time IP address asks for WSDL + true + end + + # @return [Boolean] Whether an element exists at the path + def element?(path) + [path] + true + rescue NoElementAtPath + false + end + + # Extract value from path api class + # @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 + # @return [String] Value at path + def [](path) + exchange_handler.value_from_path(response, path.to_s) + end + + # Set a parameter request in the request body. + # Can be used to build a request over several steps (e.g Cucumber) + # Will be used with FactoryBot + def []=(key, value) + @override_parameters[:body] ||= {} + @override_parameters[:body][key] = value + end + + # Implement undefined setter with []= for FactoryBot to use without needing to define params to set + # @param [Object] method_name Name of method not defined + # @param [Object] args Arguments passed to method + # @param [Object] block + def method_missing(method_name, *args, &block) + set_value = args.first + if method_name[-1] == '=' # A setter method + getter_name = method_name[0..-2] + if set_value.class < Exchange # This would be prerequisite exchange + define_singleton_method(getter_name) do + set_value + end + self[getter_name] = set_value.id if set_value.respond_to?(:id) + else + self[getter_name] = set_value + end + else + super + end + end + + # Used for setters that are not defined + def respond_to_missing?(method_name, *args) + method_name[-1] == '=' || super + end + + # Makes request, caching the response and returning self + # Used by FactoryBot + # @return [Self] + def save! + @retry_for_success = @fail_factory ? false : true + call + self + end + end \ No newline at end of file