class BingAdsReporting::Service

Constants

SUCCESS

Public Class Methods

new(settings, logger = nil) click to toggle source
# File lib/bing-ads-reporting/service.rb, line 7
def initialize(settings, logger = nil)
  @settings = settings
  @logger = logger || Logger.new($stdout)
end

Public Instance Methods

generate_report(report_settings, report_params) click to toggle source
# File lib/bing-ads-reporting/service.rb, line 12
def generate_report(report_settings, report_params)
  options = default_options(report_settings).merge(report_params)
  period = options[:period]
  report_type = options[:report_type]

  begin
    response = client.call(:submit_generate_report, message: {
      ns('ReportRequest') => {
        ns("Format") => options[:format],
        ns("Language") => "English",
        ns("ReportName") => options[:report_name],
        ns("ReturnOnlyCompleteData") => 'false',

        ns("Aggregation") => options[:aggregation],
        ns("Columns") => {
          ns("#{report_type}ReportColumn") => options[:columns]
        },
        ns("Scope") => {
          ns("AccountIds") => {
            'arr:long' => @settings[:accountId]
          }
        },
        ns("Time") => {
          # apparently order is important, and end date has to be before start date, wtf
          ns("CustomDateRangeEnd") => {
            ns("Day") => period.to.day,
            ns("Month") => period.to.month,
            ns("Year") => period.to.year
          },
          ns("CustomDateRangeStart") => {
            ns("Day") => period.from.day,
            ns("Month") => period.from.month,
            ns("Year") => period.from.year
          }
          # ns("PredefinedTime") => options[:time]
        }
      },
      :attributes! => {ns("ReportRequest") => {
                                               "i:type" => ns("#{report_type}ReportRequest"),
                                               "i:nil" => 'false'
                                               }
      }
    })
    
  rescue Savon::SOAPFault => e
    msg = 'unexpected error'
    err = e.to_hash[:fault][:detail][:ad_api_fault_detail][:errors][:ad_api_error][:error_code] rescue nil
    msg = e.to_hash[:fault][:detail][:ad_api_fault_detail][:errors][:ad_api_error][:message] if err
    if err.nil?
      err = e.to_hash[:fault][:detail][:api_fault_detail][:operation_errors][:operation_error][:error_code] rescue nil
      msg = e.to_hash[:fault][:detail][:api_fault_detail][:operation_errors][:operation_error][:message] if err
    end
    if err == 'AuthenticationTokenExpired'
      @logger.error err
      raise AuthenticationTokenExpired.new(msg)
    end
    @logger.error e.message
    @logger.error msg
    raise e
  end
  
  response.body[:submit_generate_report_response][:report_request_id]
end
report_body(id) click to toggle source

returns nil if there is no data

# File lib/bing-ads-reporting/service.rb, line 84
def report_body(id)
  download(report_url(id))
end
report_ready?(id) click to toggle source
# File lib/bing-ads-reporting/service.rb, line 76
def report_ready?(id)
  polled = poll_report(id)
  status = polled.body[:poll_generate_report_response][:report_request_status][:status] rescue nil
  raise "Report status: Error for ID: #{id}. TrackingId: #{polled.header[:tracking_id]}" if status == "Error"
  status == SUCCESS
end

Private Instance Methods

client() click to toggle source
# File lib/bing-ads-reporting/service.rb, line 134
def client
  if @settings[:username] && @settings[:password]
    header = {ns('ApplicationToken') => @settings[:applicationToken],
                                        ns('CustomerAccountId') => @settings[:accountId],
                                        ns('CustomerId') => @settings[:customerId],
                                        ns('DeveloperToken') => @settings[:developerToken],
                                        ns('UserName') => @settings[:username],
                                        ns('Password') => @settings[:password] }
  else
    header = {ns('ApplicationToken') => @settings[:applicationToken],
                                        ns('CustomerAccountId') => @settings[:accountId],
                                        ns('CustomerId') => @settings[:customerId],
                                        ns('DeveloperToken') => @settings[:developerToken],
                                        ns('AuthenticationToken') => @settings[:authenticationToken] }
  end
  Savon.client({
                wsdl: "https://reporting.api.bingads.microsoft.com/Api/Advertiser/Reporting/V11/ReportingService.svc?singleWsdl",
                log_level: :info,
                namespaces: { "xmlns:arr" => 'http://schemas.microsoft.com/2003/10/Serialization/Arrays',
                              "xmlns:i" => "http://www.w3.org/2001/XMLSchema-instance" },
                soap_header: header
                })
                # .merge({pretty_print_xml: true, log_level: :debug, log: true, logger: @logger})) # for more logging
end
default_options(report_settings) click to toggle source
# File lib/bing-ads-reporting/service.rb, line 99
def default_options(report_settings)
  { format: report_settings[:report_format],
    columns: report_settings[:columns],
    aggregation: report_settings[:aggregation],
    report_type: report_settings[:report_type],
    report_name: "MyReport" }
end
download(url) click to toggle source
# File lib/bing-ads-reporting/service.rb, line 126
def download(url)
  return unless url
  @logger.debug "Downloading Bing report from: #{url}"
  curl = Curl::Easy.new(url)
  curl.perform
  curl.body_str
end
ns(str) click to toggle source
# File lib/bing-ads-reporting/service.rb, line 159
def ns(str)
  "tns:#{str}"
end
poll_report(id) click to toggle source
# File lib/bing-ads-reporting/service.rb, line 107
def poll_report(id)
  begin
    client.call(:poll_generate_report, message: {
      ns("ReportRequestId") => id,
    })
  rescue Savon::SOAPFault => e
    err = e.to_hash[:fault][:detail][:ad_api_fault_detail][:errors][:ad_api_error][:error_code] rescue nil
    msg = e.to_hash[:fault][:detail][:ad_api_fault_detail][:errors][:ad_api_error][:message] if err
    if err.nil?
      err = e.to_hash[:fault][:detail][:api_fault_detail][:operation_errors][:operation_error][:error_code] rescue nil
      msg = e.to_hash[:fault][:detail][:api_fault_detail][:operation_errors][:operation_error][:message] if err
    end
    if err == 'AuthenticationTokenExpired'
      @logger.error err
      raise AuthenticationTokenExpired.new(msg)
    end
  end
end
report_url(id) click to toggle source
# File lib/bing-ads-reporting/service.rb, line 90
def report_url(id)
  polled = poll_report(id)
  status = polled.body[:poll_generate_report_response][:report_request_status][:status] rescue nil
  download_url = polled.body[:poll_generate_report_response][:report_request_status][:report_download_url] rescue nil
  return nil if download_url.nil? && status == SUCCESS
  raise "Report URL is not available for report id #{id}" unless download_url
  download_url
end