adCenter Perl Example Code Issues

Some of you may have noticed that the Perl example code in the adCenter API documentation does not work correctly with the latest version of SOAP::Lite from CPAN. Our example code was created and tested using SOAP::Lite version 0.60. When SOAP::Lite version 0.710 was released, our Perl code broke. The problem was in the creation of the SOAP arrays, such as the columns in a report request. With SOAP::Lite V 0.60, the following code would correctly create the SOAP array:

 

my $columns = SOAP::Data->name("Columns" =>

[

SOAP::Data->name("KeywordPerformanceReportColumn" => "AccountName"),

SOAP::Data->name("KeywordPerformanceReportColumn" => "CampaignName"),

SOAP::Data->name("KeywordPerformanceReportColumn" => "Keyword"),

SOAP::Data->name("KeywordPerformanceReportColumn" => "TimePeriod"),

SOAP::Data->name("KeywordPerformanceReportColumn" => "Impressions"),

SOAP::Data->name("KeywordPerformanceReportColumn" => "Conversions"),

]);

 

When you upgrade to SOAP::Lite V 0.710, this code will compile and run, but it does not create the SOAP array correctly. To make this code work with SOAP::Lite V 0.710, it needs to be modified to this:

 

my $columns = SOAP::Data->name("Columns" => \SOAP::Data->value

(

SOAP::Data->name("KeywordPerformanceReportColumn" => "AccountName"),

SOAP::Data->name("KeywordPerformanceReportColumn" => "CampaignName"),

SOAP::Data->name("KeywordPerformanceReportColumn" => "Keyword"),

SOAP::Data->name("KeywordPerformanceReportColumn" => "TimePeriod"),

SOAP::Data->name("KeywordPerformanceReportColumn" => "Impressions"),

SOAP::Data->name("KeywordPerformanceReportColumn" => "Conversions"),

));

 

The difference is the use of the SOAP::Data->value() method to set the array members instead of using the square bracket construct.

 

Strohm Armstrong

Programming Writer ? adCenter API

 

# For release code, use the following SOAP::Lite statement.
#use SOAP::Lite ( +maptype => {} );
# For debugging code, use the following SOAP::Lite statement.
use SOAP::Lite ( +trace => all, maptype => {} );

use LWP::Simple;
use File::Basename;
use strict;

eval
{
    my $username;
    my $password;
    my $devtoken;
    my $accountID;
    my $zipFile;
   
    # Get the parameters from the program arguments.
    unless (@ARGV == 5)
    {
        print "Usage: ";
        print "file.pl username password devtoken accountID reportfile.zip\n";
        die;
    }
    $username = $ARGV[0];
    $password = $ARGV[1];
    $devtoken = $ARGV[2];
    $accountID = $ARGV[3];
    $zipFile = $ARGV[4];
   
    # Use either the sandbox or production URI.
    # This example is for the sandbox URI.
    my $URI = "https://sandboxapi.adcenter.microsoft.com/api/advertiser/v5.1";
    # The following commented-out line contains the production URI.
    #my $URI = https://adcenterapi.microsoft.com/api/advertiser/v5.1";
   
    # The adCenter API namespace.
    my $xmlns = "https://adcenter.microsoft.com/api/advertiser/v5";
   
    # The proxy for the reporting Web service.
    my $reportingProxy = $URI."/Reporting/ReportingService.svc?wsdl";
   
    # The common data-type namespace.
    my $namespaceArrays =
        "https://schemas.microsoft.com/2003/10/Serialization/Arrays";
   
    # The service operation that will be called.
    my $action = 'QueueReport';
   
    # The Web service.
    my $reportingService = SOAP::Lite->new(
        uri => $URI,
        proxy => $reportingProxy,
        on_action => ( sub { return $action } ));
    $reportingService->autotype(0);
    $reportingService->multirefinplace(1);
    $reportingService->readable(1);
   
    my $ReportRequest = CreateKeywordPerformanceReportRequest(
        'My Keyword Report',
        $accountID);
   
    my $reportRequestId;
   
    # Assemble the service operation parameters.
    my @header = CreateV5Header($username, $password, $devtoken);
    my @params =
    (
        @header,
        $ReportRequest
    );
   
    # Make the adCenter API call.
    my $methodQueueReport = SOAP::Data->name
    (
        $action."Request"
    )->attr({xmlns => $xmlns});
   
    my $response = $reportingService->call($methodQueueReport => @params);
   
    # Check for errors.
    if ($response->fault)
    {
        print "$action failed.\n";
       
        # Display the fault code and the fault string.
        print $response->faultcode, " ", $response->faultstring, "\n";
   
        # Display the adCenter tracking ID.
        my $trackingId = $response->valueof("//ApiFaultDetail/TrackingId");
        print "TrackingId: $trackingId\n";
       
        # Display adCenter operation errors.
        my @operationErrors = $response->valueof(
            "//ApiFaultDetail/OperationErrors/OperationError");
        foreach my $operationError (@operationErrors)
        {
            print "Operation error ($operationError->{Code}) encountered. ";
            print "$operationError->{Message}\n";       
        }
    }
    else
    {
        print "Successful call to $action.\n";
   
        # Display adCenter tracking ID.
        my $trackingId = $response->valueof(
            '//ApiCallTrackingData/TrackingId');
        print "TrackingId: $trackingId\n";
       
        my $reportId = $response->valueof('//ReportRequestId');
        $reportRequestId = $reportId;
       
        RetrieveReport($reportId, $zipFile);
    }
   
    sub CreateV5Header
    {
        # Method parameters.
        my ($username, $password, $devtoken) = @_;
       
        # Initialize the application token.
        my $AppToken = SOAP::Header->name
        (
            "ApplicationToken"=>{Value => ""}
        )->attr({xmlns => $xmlns});
       
        # Initialize the developer token.
        my $DevToken =SOAP::Header->name
        (
            "DeveloperToken"=>{Value=>$devtoken}
        )->attr({xmlns => $xmlns});
       
        # Initialize the user credentials.
        my $UserCreds = SOAP::Header->name("UserCredentials" =>
        {
            Username=>SOAP::Data->name("Username" => $username),
            Password=>SOAP::Data->name("Password" => $password)
        })-> attr({xmlns => $xmlns});
   
        # Assemble the header parameters.
        my @headerParams =
        (
            $DevToken,
            $AppToken,
            $UserCreds,
        );
       
        return @headerParams;
    }
   
    # This subroutine creates the keyword performance report request. Some of
    # the data is passed in to the subrotine, but the majority of it is
    # hard-coded.
    sub CreateKeywordPerformanceReportRequest
    {
        my ($nameOfReport, $accountForReport) = @_;
       
        # Use the English language.
        my $language = SOAP::Data->name("Language" => "English");
       
        # Request an XML report.
        my $format = SOAP::Data->name("Format" => "Xml");
   
        # Allow partial data to be returned.
        my $returnOnlyCompleteData = SOAP::Data->name(
            "ReturnOnlyCompleteData" => "false");
       
        # Specify the report name.
        my $reportName = SOAP::Data->name("ReportName" => $nameOfReport);
       
        # Specify the report aggregation.
        my $aggregation = SOAP::Data->name("Aggregation" => "Monthly");
       
        # Specify the timeframe of the report. For this example, it is the
        # last six months.
        my $time = SOAP::Data->name("Time" => \SOAP::Data->value
        (
            #SOAP::Data->name("CustomDateRangeEnd" => ""),
            #SOAP::Data->name("CustomDateRangeStart" => ""),
            #SOAP::Data->name("CustomDates" => ""),
            SOAP::Data->name("PredefinedTime" => "LastSixMonths")
        ));
       
        # Specify the account that the report is for.
        my $accountIds = SOAP::Data->name("AccountIds" => \SOAP::Data->value
        (
            SOAP::Data->name("int")->value($accountForReport)->uri($namespaceArrays)
        ));
       
        my $scope = SOAP::Data->name("Scope" =>  \SOAP::Data->value($accountIds));
   
        # Specify the columns that will be in the report.
        my $columns = SOAP::Data->name("Columns" => \SOAP::Data->value
        (
            SOAP::Data->name("KeywordPerformanceReportColumn" => "AccountName"),
            SOAP::Data->name("KeywordPerformanceReportColumn" => "CampaignName"),
            SOAP::Data->name("KeywordPerformanceReportColumn" => "Keyword"),
            SOAP::Data->name("KeywordPerformanceReportColumn" => "TimePeriod"),
            SOAP::Data->name("KeywordPerformanceReportColumn" => "Impressions"),
            SOAP::Data->name("KeywordPerformanceReportColumn" => "Conversions"),
        ));
       
        # Specify the filter for the report. The report request
        # in this example specifies only the search ads that were
        # displayed in the United States.
        my $filter = SOAP::Data->name("Filter" => \SOAP::Data->value
        (
            SOAP::Data->name("AdDistribution" => "Search"),
            #SOAP::Data->name("DeliveredMatchType" => ""),
            #SOAP::Data->name("Keywords" => ""),
            SOAP::Data->name("LanguageAndRegion" => "UnitedStates")
        ));
       
        # Create the report request object.
        my $request = SOAP::Data->name("ReportRequest" => \SOAP::Data->value
        (
            $format,
            $language,
            $reportName,
            $returnOnlyCompleteData,
            $aggregation,
            $columns,
            $filter,
            $scope,
            $time
        ));
   
        # Set the request attributes.
        $request->attr
        ({
            'i:type' => 'KeywordPerformanceReportRequest',
            'xmlns:i' => 'https://www.w3.org/2001/XMLSchema-instance'
        });
       
        return $request;
    }
   
   
    # This subroutine calls the GetReportStatus service operation.
    sub GetReportStatus
    {
        my $reportRequestId=$_[0];
       
        my @header = CreateV5Header($username, $password, $devtoken);
       
        # This is important because the on_action property of the Web
        # service uses a reference to this global variable. If you don't
        # change the global variable, the wrong service operation might be
        # called.
        $action = "GetReportStatus";
       
        my @params =
        (
            @header,
            SOAP::Data->name("ReportRequestId" => $reportRequestId)
        );
       
        my $methodGetReportStatus = SOAP::Data->name(
            $action.'Request')->attr({xmlns => $xmlns});
        my $response = $reportingService->call(
            $methodGetReportStatus => @params);
   
        if ($response->fault)
        {
            print "$action failed.\n";
           
            # Display the fault code and the fault string.
            print $response->faultcode, " ", $response->faultstring, "\n";
       
            # Display adCenter tracking ID.
            my $trackingId =
                $response->valueof("//ApiFaultDetail/TrackingId");
            print "TrackingId: $trackingId\n";
           
            # Display adCenter operation errors.
            my @operationErrors = $response->valueof(
                "//ApiFaultDetail/OperationErrors/OperationError");
            foreach my $operationError (@operationErrors)
            {
                print
                    "Operation error ($operationError->{Code}) encountered. ";
                print "$operationError->{Message}\n";       
            }
        }
        else
        {
            print "Successful call to $action.\n";
        }
   
        return $response;
    }
   
    # This subroutine retrieves a requested report. This subroutine will work
    # for any type of report. The only parameter needed for the subroutine is
    # the report request identifier.
    sub RetrieveReport
    {
        my ($reportRequestId, $zipFileName) = @_;
        my $polling = 1;
        my $waitMinutes = 1;
        my $content = "";
   
        while ($polling)
        {
            my $response = GetReportStatus($reportRequestId);
            my $status = $response->valueof('//Status');
            print "Status: $status \n\r";
           
            if ($status =~ /Success/)
            {
                # Get the URL of the report to download.
                my $downloadUrl = $response->valueof('//ReportDownloadUrl');
                print "Downloading From : $downloadUrl\n\r";
   
                # Download the content of the zipped report.
                unless (defined ($content = get($downloadUrl)))
                {
                    die;
                }
   
                # Make sure that the ZIP file destination directory exists.
                my $dirname = dirname($zipFileName);
                mkdir($dirname);
               
                # Open the ZIP file for writing and in binary mode.
                open(ZIPFILE, ">", $zipFileName);
                binmode(ZIPFILE);
               
                # Write the report contents to the ZIP file.
                print(ZIPFILE $content);
                close(ZIPFILE);
               
                $polling = 0;
            }
           
            if (!($status =~ /Pending/))
            {
                $polling = 0;
            }
   
            if($polling)
            {
                # Wait a while before getting the status again.
                sleep($waitMinutes * 60);
            }
        }
   
        return $content;
    }
   
};
warn $@ if $@;