class AdsCommon::SavonService
Constants
- FALLBACK_API_ERROR_EXCEPTION
- MAX_FAULT_LOG_LENGTH
- REDACTED_STR
Attributes
Public Class Methods
Creates a new service.
# File lib/ads_common/savon_service.rb, line 39 def initialize(config, endpoint, namespace, version) if self.class() == AdsCommon::SavonService raise NoMethodError, 'Tried to instantiate an abstract class' end @config, @version, @namespace = config, version, namespace @client = create_savon_client(endpoint, namespace) @xml_only = false end
Private Instance Methods
Creates and sets up Savon client.
# File lib/ads_common/savon_service.rb, line 66 def create_savon_client(endpoint, namespace) client = GoogleAdsSavon::Client.new do |wsdl, httpi| wsdl.endpoint = endpoint wsdl.namespace = namespace AdsCommon::Http.configure_httpi(@config, httpi) end client.config.raise_errors = false client.config.logger.subject = NoopLogger.new return client end
Log the request, response, and summary lines.
# File lib/ads_common/savon_service.rb, line 169 def do_logging(action, request, response) logger = get_logger() return unless should_log_summary(logger.level, response.soap_fault?) response_hash = response.hash soap_headers = {} begin soap_headers = response_hash[:envelope][:header][:response_header] rescue NoMethodError # If there are no headers, just ignore. We'll log what we know. end summary_message = ('ID: %s, URL: %s, Service: %s, Action: %s, Response ' + 'time: %sms, Request ID: %s') % [@header_handler.identifier, request.url, self.class.to_s.split("::").last, action, soap_headers[:response_time], soap_headers[:request_id]] if soap_headers[:operations] summary_message += ', Operations: %s' % soap_headers[:operations] end summary_message += ', Is fault: %s' % response.soap_fault? request_message = nil response_message = nil if should_log_payloads(logger.level, response.soap_fault?) request_message = 'Outgoing request: %s %s' % [format_headers(request.headers), sanitize_request(request.body)] response_message = 'Incoming response: %s %s' % [format_headers(response.http.headers), response.http.body] end if response.soap_fault? summary_message += ', Fault message: %s' % format_fault( response_hash[:envelope][:body][:fault][:faultstring]) logger.warn(summary_message) logger.info(request_message) unless request_message.nil? logger.info(response_message) unless response_message.nil? else logger.info(summary_message) logger.debug(request_message) unless request_message.nil? logger.debug(response_message) unless response_message.nil? end end
Finds an exception object for a given response.
# File lib/ads_common/savon_service.rb, line 130 def exception_for_soap_fault(response) begin fault = response[:fault] if fault[:detail] and fault[:detail][:api_exception_fault] exception_fault = fault[:detail][:api_exception_fault] exception_name = ( exception_fault[:application_exception_type] || FALLBACK_API_ERROR_EXCEPTION) exception_class = get_module().const_get(exception_name) return exception_class.new(exception_fault) elsif fault[:faultstring] fault_message = fault[:faultstring] return AdsCommon::Errors::ApiException.new( "Unknown exception with error: %s" % fault_message) else raise ArgumentError.new(fault.to_s) end rescue Exception => e return AdsCommon::Errors::ApiException.new( "Failed to resolve exception (%s), SOAP fault: %s" % [e.message, response.soap_fault]) end end
Executes SOAP action specified as a string with given arguments.
# File lib/ads_common/savon_service.rb, line 87 def execute_action(action_name, args, &block) registry = get_service_registry() validator = ParametersValidator.new(registry) args = validator.validate_args(action_name, args) request_info, response = handle_soap_request( action_name.to_sym, false, args, validator.extra_namespaces) do_logging(action_name, request_info, response) handle_errors(response) extractor = ResultsExtractor.new(registry) result = extractor.extract_result(response, action_name, &block) run_user_block(extractor, response, result, &block) if block_given? return result end
Format the fault message by capping length and removing newlines.
# File lib/ads_common/savon_service.rb, line 229 def format_fault(message) if message.length > MAX_FAULT_LOG_LENGTH message = message[0, MAX_FAULT_LOG_LENGTH] end return message.gsub("\n", ' ') end
Format headers, redacting sensitive information.
# File lib/ads_common/savon_service.rb, line 215 def format_headers(headers) return headers.map do |k, v| v = REDACTED_STR if k == 'Authorization' [k, v].join(': ') end.join(', ') end
Returns currently configured Logger.
# File lib/ads_common/savon_service.rb, line 51 def get_logger() return @config.read('library.logger') end
Returns Module for the current service. Has to be overridden.
# File lib/ads_common/savon_service.rb, line 61 def get_module() raise NoMethodError, 'This method needs to be overridden' end
Returns ServiceRegistry for the current service. Has to be overridden.
# File lib/ads_common/savon_service.rb, line 56 def get_service_registry() raise NoMethodError, 'This method needs to be overridden' end
Generates and returns SOAP XML for the specified action and args.
# File lib/ads_common/savon_service.rb, line 78 def get_soap_xml(action_name, args) registry = get_service_registry() validator = ParametersValidator.new(registry) args = validator.validate_args(action_name, args) return handle_soap_request( action_name.to_sym, true, args, validator.extra_namespaces) end
Checks for errors in response and raises appropriate exception.
# File lib/ads_common/savon_service.rb, line 118 def handle_errors(response) if response.soap_fault? exception = exception_for_soap_fault(response) raise exception end if response.http_error? raise AdsCommon::Errors::HttpError, "HTTP Error occurred: %s" % response.http_error end end
Executes the SOAP request with original SOAP name.
# File lib/ads_common/savon_service.rb, line 102 def handle_soap_request(action, xml_only, args, extra_namespaces) original_action_name = get_service_registry.get_method_signature(action)[:original_name] original_action_name = action if original_action_name.nil? request_info = nil response = @client.request(original_action_name) do |soap, wsdl, http| soap.body = args @header_handler.prepare_request(http, soap) soap.namespaces.merge!(extra_namespaces) unless extra_namespaces.nil? return soap.to_xml if xml_only request_info = RequestInfo.new(soap.to_xml, http.headers, http.url) end return request_info, response end
Yields to user-specified block with additional information such as headers.
# File lib/ads_common/savon_service.rb, line 156 def run_user_block(extractor, response, body, &block) header = extractor.extract_header_data(response) case block.arity when 1 then yield(header) when 2 then yield(header, body) else raise AdsCommon::Errors::ApiException, "Wrong number of block parameters: %d" % block.arity end return nil end
Sanitize the request body, redacting sensitive information.
# File lib/ads_common/savon_service.rb, line 223 def sanitize_request(body) return body.gsub(/(developerToken>|httpAuthorizationHeader>)[^<]+(<\/)/, '\1' + REDACTED_STR + '\2') end
Check whether or not to log payloads based on log level.
# File lib/ads_common/savon_service.rb, line 245 def should_log_payloads(level, is_fault) # Fault payloads log at INFO. return level <= Logger::INFO if is_fault # Success payloads log at DEBUG. return level <= Logger::DEBUG end
Check whether or not to log request summaries based on log level.
# File lib/ads_common/savon_service.rb, line 237 def should_log_summary(level, is_fault) # Fault summaries log at WARN. return level <= Logger::WARN if is_fault # Success summaries log at INFO. return level <= Logger::INFO end