lib/soaspec/exchange_handlers/soap_handler.rb in soaspec-0.1.5 vs lib/soaspec/exchange_handlers/soap_handler.rb in soaspec-0.1.6
- old
+ new
@@ -1,237 +1,237 @@
-
-require_relative 'exchange_handler'
-require_relative '../core_ext/hash'
-require_relative '../not_found_errors'
-require_relative 'handler_accessors'
-require_relative '../interpreter'
-require 'forwardable'
-
-module Soaspec
-
- # Accessors specific to SOAP handler
- module SoapAccessors
- # Define attributes set on root SOAP element
- def root_attributes(attributes)
- define_method('request_root_attributes') do
- attributes
- end
- end
- end
-
- # Wraps around Savon client defining default values dependent on the soap request
- class SoapHandler < ExchangeHandler
- extend Soaspec::SoapAccessors
- extend Forwardable
-
- delegate [:operations] => :client
-
- # Savon client used to make SOAP calls
- attr_accessor :client
- # SOAP Operation to use by default
- attr_accessor :operation
-
- # Attributes set at the root XML element of SOAP request
- def request_root_attributes
- nil
- end
-
- # Options to log xml request and response
- def logging_options
- {
- log: true, # See request and response. (Put this in traffic file)
- log_level: :debug,
- logger: Soaspec::SpecLogger.create,
- pretty_print_xml: true # Prints XML pretty
- }
- end
-
- # Default Savon options. See http://savonrb.com/version2/globals.html for details
- # @return [Hash] Default Savon options for all BasicSoapHandler
- def default_options
- {
- ssl_verify_mode: :none, # Easier for testing. Not so secure
- follow_redirects: true, # Necessary for many API calls
- soap_version: 2, # use SOAP 1.2. You will get 415 error if this is incorrect
- raise_errors: false # HTTP errors not cause failure as often negative test scenarios expect not 200 response
- # Things could go wrong if not set properly
- # env_namespace: :soap, # Change environment namespace
- # namespace_identifier: :tst, # Change namespace element
- # element_form_default: :qualified # Populate each element with namespace
- # namespace: 'http://Extended_namespace.xsd' change root namespace
- # basic_auth: 'user', 'password'
- }
- end
-
- # Add values to here when extending this class to have default Savon options.
- # See http://savonrb.com/version2/globals.html for details
- # @return [Hash] Savon options adding to & overriding defaults
- def savon_options
- {
- }
- end
-
- # Setup object to handle communicating with a particular SOAP WSDL
- # @param [Hash] options Options defining SOAP request. WSDL, authentication, see http://savonrb.com/version2/globals.html for list of options
- def initialize(name = self.class.to_s, options = {})
- @default_hash = {}
- @request_option = :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, :operation)
- set_remove_key(options, :default_hash)
- set_remove_key(options, :template_name)
- merged_options = Soaspec.log_api_traffic? ? default_options.merge(logging_options) : default_options
- merged_options.merge! savon_options
- merged_options.merge!(options)
- self.client = Savon.client(merged_options)
- end
-
- # Used in making request via hash or in template via Erb
- def request_body_params(request_parameters)
- test_values = request_parameters[:body] || request_parameters
- test_values.transform_keys_to_symbols if Soaspec.always_use_keys?
- end
-
- # Used in together with Exchange request that passes such override parameters
- # @param [Hash] request_parameters Parameters used to overwrite defaults in request
- def make_request(request_parameters)
- test_values = request_body_params request_parameters
- begin
- if @request_option == :template
- request_body = File.read(File.join(Soaspec.template_folder, template_name))
- render_body = ERB.new(request_body).result(binding)
- client.call(operation, xml: render_body) # Call the SOAP operation with the request XML provided
- elsif @request_option == :hash
- client.call(operation, message: @default_hash.merge(test_values), attributes: request_root_attributes)
- end
- rescue Savon::HTTPError => e
- e
- end
- end
-
- # @param [Hash] format Format of expected result
- # @return [Object] Generic body to be displayed in error messages
- def response_body(response, format: :hash)
- case format
- when :hash
- response.body
- when :raw
- response.xml
- else
- response.body
- end
- end
-
- # @return [Boolean] Whether the request found the desired value or not
- def found?(response)
- status_code_for(response) != 404
- end
-
- # Response status code for response. '200' indicates a success
- # @param [Savon::Response] response
- # @return [Integer] Status code
- def status_code_for(response)
- response.http.code
- end
-
- # @return [Boolean] Whether response includes provided string within it
- def include_in_body?(response, expected)
- response.to_xml.to_s.include? expected
- end
-
- # @param [Symbol] expected
- # @return [Boolean] Whether response body contains expected key
- def include_key?(response, expected)
- body = response.body
- body.extend Hashie::Extensions::DeepFind
- !body.deep_find_all(expected).empty?
- end
-
- # Convert all XML nodes to lowercase
- # @param [Nokogiri::XML::Document]
- def convert_to_lower_case(xml_doc)
- xml_doc.traverse do |node|
- node.name = node.name.downcase if node.kind_of?(Nokogiri::XML::Element)
- end
- end
-
- # Returns the value at the provided xpath
- # @param [Savon::Response] response
- # @param [String] xpath
- # @return [Enumerable] Elements found through Xpath
- def xpath_elements_for(response: nil, xpath: nil, attribute: nil)
- raise ArgumentError('response and xpath must be passed to method') unless response && xpath
- xpath = "//*[@#{attribute}]" unless attribute.nil?
- xpath = '//' + xpath if xpath[0] != '/'
- temp_doc = response.doc.dup
- convert_to_lower_case(temp_doc) if convert_to_lower?
- if strip_namespaces? && !xpath.include?(':')
- temp_doc.remove_namespaces!
- temp_doc.xpath(xpath)
- else
- temp_doc.xpath(xpath, temp_doc.collect_namespaces)
- end
- end
-
- # Based on a exchange, return the value at the provided xpath
- # If the path does not begin with a '/', a '//' is added to it
- # @param [Savon::Response] response
- # @param [String] path Xpath
- # @param [String] attribute Generic attribute to find. Will override path
- # @return [String] Value at Xpath
- def value_from_path(response, path, attribute: nil)
- results = xpath_elements_for(response: response, xpath: path, attribute: attribute)
- raise NoElementAtPath, "No value at Xpath '#{path}' in XML #{response.doc}" if results.empty?
- return results.first.inner_text if attribute.nil?
- results.first.attributes[attribute].inner_text
- end
-
- # @return [Enumerable] List of values returned from path
- def values_from_path(response, path, attribute: nil)
- xpath_elements_for(response: response, xpath: path, attribute: attribute).map(&:inner_text)
- end
-
- # alias elements xpath_elements_for
-
- # @return [Boolean] Whether any of the keys of the Body Hash include value
- def include_value?(response, expected_value)
- response.body.include_value?(expected_value)
- end
-
- # Hash of response body
- def to_hash(response)
- response.body
- end
-
- # Convenience methods for once off usage of a SOAP request
- class << self
-
- # 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)
- tmp_class = new(method_name)
- operations = tmp_class.operations
- if operations.include? method_name
- tmp_class.operation = method_name
- exchange = Exchange.new(method_name, *args)
- exchange.exchange_handler = tmp_class
- exchange
- else
- super
- end
- end
-
- def respond_to_missing?(method_name, *args)
- tmp_class = new(args)
- operations = tmp_class.operations
- operations.include?(method_name) || super
- end
- end
-
- end
+
+require_relative 'exchange_handler'
+require_relative '../core_ext/hash'
+require_relative '../not_found_errors'
+require_relative 'handler_accessors'
+require_relative '../interpreter'
+require 'forwardable'
+
+module Soaspec
+
+ # Accessors specific to SOAP handler
+ module SoapAccessors
+ # Define attributes set on root SOAP element
+ def root_attributes(attributes)
+ define_method('request_root_attributes') do
+ attributes
+ end
+ end
+ end
+
+ # Wraps around Savon client defining default values dependent on the soap request
+ class SoapHandler < ExchangeHandler
+ extend Soaspec::SoapAccessors
+ extend Forwardable
+
+ delegate [:operations] => :client
+
+ # Savon client used to make SOAP calls
+ attr_accessor :client
+ # SOAP Operation to use by default
+ attr_accessor :operation
+
+ # Attributes set at the root XML element of SOAP request
+ def request_root_attributes
+ nil
+ end
+
+ # Options to log xml request and response
+ def logging_options
+ {
+ log: true, # See request and response. (Put this in traffic file)
+ log_level: :debug,
+ logger: Soaspec::SpecLogger.create,
+ pretty_print_xml: true # Prints XML pretty
+ }
+ end
+
+ # Default Savon options. See http://savonrb.com/version2/globals.html for details
+ # @return [Hash] Default Savon options for all BasicSoapHandler
+ def default_options
+ {
+ ssl_verify_mode: :none, # Easier for testing. Not so secure
+ follow_redirects: true, # Necessary for many API calls
+ soap_version: 2, # use SOAP 1.2. You will get 415 error if this is incorrect
+ raise_errors: false # HTTP errors not cause failure as often negative test scenarios expect not 200 response
+ # Things could go wrong if not set properly
+ # env_namespace: :soap, # Change environment namespace
+ # namespace_identifier: :tst, # Change namespace element
+ # element_form_default: :qualified # Populate each element with namespace
+ # namespace: 'http://Extended_namespace.xsd' change root namespace
+ # basic_auth: 'user', 'password'
+ }
+ end
+
+ # Add values to here when extending this class to have default Savon options.
+ # See http://savonrb.com/version2/globals.html for details
+ # @return [Hash] Savon options adding to & overriding defaults
+ def savon_options
+ {
+ }
+ end
+
+ # Setup object to handle communicating with a particular SOAP WSDL
+ # @param [Hash] options Options defining SOAP request. WSDL, authentication, see http://savonrb.com/version2/globals.html for list of options
+ def initialize(name = self.class.to_s, options = {})
+ @default_hash = {}
+ @request_option = :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, :operation)
+ set_remove_key(options, :default_hash)
+ set_remove_key(options, :template_name)
+ merged_options = Soaspec.log_api_traffic? ? default_options.merge(logging_options) : default_options
+ merged_options.merge! savon_options
+ merged_options.merge!(options)
+ self.client = Savon.client(merged_options)
+ end
+
+ # Used in making request via hash or in template via Erb
+ def request_body_params(request_parameters)
+ test_values = request_parameters[:body] || request_parameters
+ test_values.transform_keys_to_symbols if Soaspec.always_use_keys?
+ end
+
+ # Used in together with Exchange request that passes such override parameters
+ # @param [Hash] request_parameters Parameters used to overwrite defaults in request
+ def make_request(request_parameters)
+ test_values = request_body_params request_parameters
+ begin
+ if @request_option == :template
+ request_body = File.read(File.join(Soaspec.template_folder, template_name))
+ render_body = ERB.new(request_body).result(binding)
+ client.call(operation, xml: render_body) # Call the SOAP operation with the request XML provided
+ elsif @request_option == :hash
+ client.call(operation, message: @default_hash.merge(test_values), attributes: request_root_attributes)
+ end
+ rescue Savon::HTTPError => e
+ e
+ end
+ end
+
+ # @param [Hash] format Format of expected result
+ # @return [Object] Generic body to be displayed in error messages
+ def response_body(response, format: :hash)
+ case format
+ when :hash
+ response.body
+ when :raw
+ response.xml
+ else
+ response.body
+ end
+ end
+
+ # @return [Boolean] Whether the request found the desired value or not
+ def found?(response)
+ status_code_for(response) != 404
+ end
+
+ # Response status code for response. '200' indicates a success
+ # @param [Savon::Response] response
+ # @return [Integer] Status code
+ def status_code_for(response)
+ response.http.code
+ end
+
+ # @return [Boolean] Whether response includes provided string within it
+ def include_in_body?(response, expected)
+ response.to_xml.to_s.include? expected
+ end
+
+ # @param [Symbol] expected
+ # @return [Boolean] Whether response body contains expected key
+ def include_key?(response, expected)
+ body = response.body
+ body.extend Hashie::Extensions::DeepFind
+ !body.deep_find_all(expected).empty?
+ end
+
+ # Convert all XML nodes to lowercase
+ # @param [Nokogiri::XML::Document]
+ def convert_to_lower_case(xml_doc)
+ xml_doc.traverse do |node|
+ node.name = node.name.downcase if node.kind_of?(Nokogiri::XML::Element)
+ end
+ end
+
+ # Returns the value at the provided xpath
+ # @param [Savon::Response] response
+ # @param [String] xpath
+ # @return [Enumerable] Elements found through Xpath
+ def xpath_elements_for(response: nil, xpath: nil, attribute: nil)
+ raise ArgumentError('response and xpath must be passed to method') unless response && xpath
+ xpath = "//*[@#{attribute}]" unless attribute.nil?
+ xpath = '//' + xpath if xpath[0] != '/'
+ temp_doc = response.doc.dup
+ convert_to_lower_case(temp_doc) if convert_to_lower?
+ if strip_namespaces? && !xpath.include?(':')
+ temp_doc.remove_namespaces!
+ temp_doc.xpath(xpath)
+ else
+ temp_doc.xpath(xpath, temp_doc.collect_namespaces)
+ end
+ end
+
+ # Based on a exchange, return the value at the provided xpath
+ # If the path does not begin with a '/', a '//' is added to it
+ # @param [Savon::Response] response
+ # @param [String] path Xpath
+ # @param [String] attribute Generic attribute to find. Will override path
+ # @return [String] Value at Xpath
+ def value_from_path(response, path, attribute: nil)
+ results = xpath_elements_for(response: response, xpath: path, attribute: attribute)
+ raise NoElementAtPath, "No value at Xpath '#{path}' in XML #{response.doc}" if results.empty?
+ return results.first.inner_text if attribute.nil?
+ results.first.attributes[attribute].inner_text
+ end
+
+ # @return [Enumerable] List of values returned from path
+ def values_from_path(response, path, attribute: nil)
+ xpath_elements_for(response: response, xpath: path, attribute: attribute).map(&:inner_text)
+ end
+
+ # alias elements xpath_elements_for
+
+ # @return [Boolean] Whether any of the keys of the Body Hash include value
+ def include_value?(response, expected_value)
+ response.body.include_value?(expected_value)
+ end
+
+ # Hash of response body
+ def to_hash(response)
+ response.body
+ end
+
+ # Convenience methods for once off usage of a SOAP request
+ class << self
+
+ # 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)
+ tmp_class = new(method_name)
+ operations = tmp_class.operations
+ if operations.include? method_name
+ tmp_class.operation = method_name
+ exchange = Exchange.new(method_name, *args)
+ exchange.exchange_handler = tmp_class
+ exchange
+ else
+ super
+ end
+ end
+
+ def respond_to_missing?(method_name, *args)
+ tmp_class = new(args)
+ operations = tmp_class.operations
+ operations.include?(method_name) || super
+ end
+ end
+
+ end
end
\ No newline at end of file