lib/soaspec/exchange_handlers/rest_handler.rb in soaspec-0.1.12 vs lib/soaspec/exchange_handlers/rest_handler.rb in soaspec-0.1.13
- old
+ new
@@ -12,22 +12,64 @@
module Soaspec
# Wraps around Savon client defining default values dependent on the soap request
class RestHandler < ExchangeHandler
extend Soaspec::RestAccessors
+ include Soaspec::RestAccessorsDefaults
# User used in making API calls
attr_accessor :api_username
- # Set through following method. Base URL in REST requests.
- def base_url_value
- nil
+ # Setup object to handle communicating with a particular SOAP WSDL
+ # @param [Hash] options Options defining REST request. base_url, default_hash
+ def initialize(name = self.class.to_s, options = {})
+ raise "Base URL not set! Please set in class with 'base_url' method" unless base_url_value
+ @default_hash = {}
+ if name.is_a?(Hash) && options == {} # If name is not set, use first parameter as the options hash
+ options = name
+ name = self.class.to_s
+ end
+ super
+ set_remove_keys(options, %i[api_username default_hash template_name])
+ @init_options = options
end
- # Headers used in RestClient
- def rest_client_headers
- {}
+ # Used in together with Exchange request that passes such override parameters
+ # @param [Hash] override_parameters Params to characterize REST request
+ # @option override_parameters [Hash] :params Extra parameters (E.g. headers)
+ # @option override_parameters [String] suburl URL appended to base_url of class
+ # @option override_parameters [Hash] :q Query for REST
+ # @option override_parameters [Symbol] :method REST method (:get, :post, :patch, etc)
+ # Following are for the body of the request
+ # @option override_parameters [Hash] :body Hash to be converted to JSON in request body
+ # @option override_parameters [String] :payload String to be passed directly in request body
+ # @option override_parameters [String] :template_name Path to file to be read via ERB and passed in request body
+ def make_request(override_parameters)
+ @merged_options ||= init_merge_options
+ test_values = override_parameters
+ test_values[:params] ||= {}
+ test_values[:method] ||= :post
+ test_values[:suburl] = test_values[:suburl].to_s if test_values[:suburl]
+ test_values[:params][:params] = test_values[:q] if test_values[:q] # Use q for query parameters. Nested :params is ugly and long
+ # In order for ERB to be calculated at correct time, the first time request is made, the resource should be created
+ @resource ||= RestClient::Resource.new(ERB.new(base_url_value).result(binding), @merged_options)
+
+ @resource_used = test_values[:suburl] ? @resource[test_values[:suburl]] : @resource
+
+ begin
+ response = case test_values[:method]
+ when :post, :patch, :put
+ Soaspec::SpecLogger.info("request body: #{post_data(test_values)}")
+ @resource_used.send(test_values[:method].to_s, post_data(test_values), test_values[:params])
+ else # :get, :delete
+ @resource_used.send(test_values[:method].to_s, test_values[:params])
+ end
+ rescue RestClient::ExceptionWithResponse => e
+ response = e.response
+ end
+ Soaspec::SpecLogger.info(["response_headers: #{response.headers}", "response_body: #{response}"])
+ response
end
# Add values to here when extending this class to have default REST options.
# See rest client resource at https://github.com/rest-client/rest-client for details
# It's easier to set headers via 'headers' accessor rather than here
@@ -41,39 +83,16 @@
# @return [Hash] Hash from 'rest_client_headers' passed through ERB
def parse_headers
Hash[rest_client_headers.map { |k, header| [k, ERB.new(header).result(binding)] }]
end
- # Setup object to handle communicating with a particular SOAP WSDL
- # @param [Hash] options Options defining SOAP request. WSDL, authentication
- def initialize(name = self.class.to_s, options = {})
- raise "Base URL not set! Please set in class with 'base_url' method" unless base_url_value
- @default_hash = {}
- if name.is_a?(Hash) && options == {} # If name is not set
- options = name
- name = self.class.to_s
- end
- super
- set_remove_key(options, :api_username)
- set_remove_key(options, :default_hash)
- set_remove_key(options, :template_name)
- @init_options = options
- end
-
# Convert snakecase to PascalCase
def convert_to_pascal_case(key)
return key if /[[:upper:]]/ =~ key[0] # If first character already capital, don't do conversion
key.split('_').map(&:capitalize).join
end
- # Whether to convert each key in the request to PascalCase
- # It will also auto convert simple XPath, JSONPath where '//' or '..' not specified
- # @return Whether to convert to PascalCase
- def pascal_keys?
- false
- end
-
# Initialize value of merged options
# @return [Hash] Hash of merged options
def init_merge_options
options = rest_resource_options
options[:headers] ||= {}
@@ -82,44 +101,10 @@
options[:headers][:authorization] ||= ERB.new('Bearer <%= access_token %>').result(binding)
end
options.merge(@init_options)
end
- # Used in together with Exchange request that passes such override parameters
- # @param [Hash] override_parameters Params to characterize REST request
- # @param_value [params] Extra parameters (E.g. headers)
- # @param_value [suburl] URL appended to base_url of class
- # @param_value [q] Query for REST
- # @param_value [method] REST method (:get, :post, etc)
- def make_request(override_parameters)
- @merged_options ||= init_merge_options
- test_values = override_parameters
- test_values[:params] ||= {}
- test_values[:method] ||= :post
- test_values[:suburl] = test_values[:suburl].to_s if test_values[:suburl]
- test_values[:params][:params] = test_values[:q] if test_values[:q] # Use q for query parameters. Nested :params is ugly and long
- # In order for ERB to be calculated at correct time, the first time request is made, the resource should be created
- @resource ||= RestClient::Resource.new(ERB.new(base_url_value).result(binding), @merged_options)
-
- @resource_used = test_values[:suburl] ? @resource[test_values[:suburl]] : @resource
-
- begin
- response = case test_values[:method]
- when :post, :patch, :put
- Soaspec::SpecLogger.info("request body: #{post_data(test_values)}")
- @resource_used.send(test_values[:method].to_s, post_data(test_values), test_values[:params])
- else # :get, :delete
- @resource_used.send(test_values[:method].to_s, test_values[:params])
- end
- rescue RestClient::ExceptionWithResponse => e
- response = e.response
- end
- Soaspec::SpecLogger.info('response_headers: ' + response.headers.to_s)
- Soaspec::SpecLogger.info('response_body: ' + response.to_s)
- response
- end
-
# @param [Hash] format Format of expected result.
# @return [Object] Generic body to be displayed in error messages
def response_body(response, format: :hash)
extract_hash response
end
@@ -132,29 +117,10 @@
# @@return [Boolean] Whether the request found the desired value or not
def found?(response)
status_code_for(response) != 404
end
- # Convert XML or JSON response into a Hash
- # @param [String] response Response as a String (either in XML or JSON)
- # @return [Hash]
- def extract_hash(response)
- raise ArgumentError("Empty Body. Can't assert on it") if response.body.empty?
- case Interpreter.response_type_for response
- when :json
- converted = JSON.parse(response.body)
- return converted.transform_keys_to_symbols if converted.is_a? Hash
- return converted.map!(&:transform_keys_to_symbols) if converted.is_a? Array
- raise 'Incorrect Type prodcued ' + converted.class
- when :xml
- parser = Nori.new(convert_tags_to: lambda { |tag| tag.snakecase.to_sym })
- parser.parse(response.body)
- else
- raise "Neither XML nor JSON detected. It is #{type}. Don't know how to parse It is #{response.body}"
- end
- end
-
# @return [Boolean] Whether response contains expected value
def include_value?(response, expected)
extract_hash(response).include_value? expected
end
@@ -166,29 +132,10 @@
# @return [Integer] HTTP Status code for response
def status_code_for(response)
response.code
end
- # Override this to specify elements that must be present in the response
- # Will be used in 'success_scenarios' shared examples
- # @return [Array] Array of symbols specifying element names
- def mandatory_elements
- []
- end
-
- # Override this to specify xpath results that must be present in the response
- # Will be used in 'success_scenarios' shared examples
- # @return [Hash] Hash of 'xpath' => 'expected value' pairs
- def mandatory_xpath_values
- {}
- end
-
- # Attributes set at the root XML element of SOAP request
- def root_attributes
- nil
- end
-
# Returns the value at the provided xpath
# @param [RestClient::Response] response
# @param [String] xpath
# @return [Enumerable] Value inside element found through Xpath
def xpath_elements_for(response: nil, xpath: nil, attribute: nil)
@@ -257,10 +204,30 @@
else
raise "Unable to interpret type of #{response.body}"
end
end
+ # TODO: This and 'to_hash' method should be merged
+ # Convert XML or JSON response into a Hash
+ # @param [String] response Response as a String (either in XML or JSON)
+ # @return [Hash]
+ def extract_hash(response)
+ raise ArgumentError("Empty Body. Can't assert on it") if response.body.empty?
+ case Interpreter.response_type_for response
+ when :json
+ converted = JSON.parse(response.body)
+ return converted.transform_keys_to_symbols if converted.is_a? Hash
+ return converted.map!(&:transform_keys_to_symbols) if converted.is_a? Array
+ raise 'Incorrect Type produced ' + converted.class
+ when :xml
+ parser = Nori.new(convert_tags_to: lambda { |tag| tag.snakecase.to_sym })
+ parser.parse(response.body)
+ else
+ raise "Neither XML nor JSON detected. It is #{type}. Don't know how to parse It is #{response.body}"
+ end
+ end
+
# @return [Hash] Hash representing response body
def to_hash(response)
case Interpreter.response_type_for(response)
when :xml
parser = Nori.new(strip_namespaces: strip_namespaces?, convert_tags_to: ->(tag) { tag.snakecase.to_sym })
@@ -270,19 +237,19 @@
else
raise "Unable to interpret type of #{response.body}"
end
end
- # Request of API call. Either intended request or actual request
+ # @response [RestClient::Request] Request of API call. Either intended request or actual request
def request(response)
return 'Request not yet sent' if response.nil?
response.request
end
private
- # Work out data to send based upon payload, template_name
+ # Work out data to send based upon payload, template_name, or body
# @return [String] Payload to send in REST request
def post_data(test_values)
data = if test_values[:body]
test_values[:payload] = JSON.generate(hash_used_in_request(test_values[:body])).to_s
elsif @request_option == :template
@@ -323,8 +290,7 @@
new(exchange_params)
Exchange.new(params[:name], method: rest_method.to_sym, **params)
end
end
end
-
end
-end
\ No newline at end of file
+end