# frozen_string_literal: true require_relative '../../soaspec' require_relative 'exchange_properties' require_relative 'exchange_extractor' require_relative 'request_builder' require_relative 'exchange_repeater' require_relative 'variable_storer' # 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 # # It is tied to an ExchangeHandler that needs to be defined either globally before it's created or in 'default_handler_used' class Exchange extend Soaspec::ExchangeProperties include Soaspec::ExchangeExtractor include Soaspec::RequestBuilder include Soaspec::ExchangeRepeater include Soaspec::VariableStorer # @return [ExchangeHandler] Instance of ExchangeHandler for which this exchange is made attr_accessor :exchange_handler # @return [Integer] How many times to retry for a success attr_accessor :retry_count # @return [Integer] Times request was retried before being returned attr_accessor :times_retried # @return [String] Name used for displaying class attr_accessor :test_name # @return [Boolean] Expect Factory to fail upon trying to create attr_writer :fail_factory # @return [Hash] Parameters to override for default params defined in ExchangeHandler # These are the parameters specific to the Exchange and will override, append to # what's defined in the ExchangeHandler attr_accessor :override_parameters # 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 # This is set on an individual Exchange marking it as one that should be retried # @return [Bool] Whether to keep making request until success code reached def retry_for_success? @retry_for_success end # Defined as general rule from ExchangeHandler # @return [Boolean] Whether exception is an exception that must be retried def invalid_exception? !exchange_handler.retry_on_exceptions.find { |e| e == exchange_handler.exception.class }.nil? end # Override this in subclass to tie that subclass to an ExchangeHandler # @return [Soaspec::ExchangeHandler] Soaspec::ExchangeHandler used by this exchange def default_handler_used; end # Create new Exchange according to parameters set. A response will be made if called # explicitly with 'response' method or through other methods that use it like 'status_code' # @param [Symbol, String] name Name shown in RSpec run # @param [Hash] override_parameters Parameters to override for default params (set through ExchangeHandler or Exchange class) # These are the parameters that would be specific for a test def initialize(name = self.class.to_s, override_parameters = {}) if name.is_a? Hash # Name not provided override_parameters = name name = nil end self.test_name ||= name.to_s @override_parameters = override_parameters # 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 @fail_factory = nil @retry_for_success = false self.retry_count = exchange_handler.retry_exception_limit exchange_handler.elements.each { |element| methods_for_element(element) } end # @return [Hash] Hash representing what will be sent def request_parameters exchange_handler.request_parameters(@override_parameters) 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.info 'Example ' + test_name request_params = @override_parameters (0..retry_count).each do |count| response = exchange_handler.make_request(request_params) return response if !retry_for_success? && !invalid_exception? return response if (200..299).cover? exchange_handler.status_code_for(response) sleep exchange_handler.retry_pause_time # Time before retrying self.times_retried = count break response if count == retry_count end 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 # @return [RestClient::Response,Savon::Response] Returns response object from Api. # Will make the request if not made and then cache it for later on # @example For SOAP it will be a Savon response # response.body (body of response as Hash) # response.header (head of response as Hash) # @example For REST it will be a RestClient::Response def response require 'forwardable' Soaspec.last_exchange = self @response ||= make_request @response.define_singleton_method(:exchange) { Soaspec.last_exchange } unless @response.respond_to?(:exchange) @response.extend Forwardable @response.delegate %i[value_from_path values_from_path] => :exchange @response end # @return [ResponseObject] Currently returning response object. This will change (in 0.3) to be itself to # allow easy method chaining def call response self end end