Tuesday, December 10, 2013

Handle Bing Ads API Errors or Exceptions

Last chapter, we talked about Handle Google AdWords API Errors or Exceptions . With the new Bing Ads API Version 9, The partial failure/success feature was introduced to the following operations, The PartialErrors element represents an array of BatchError objects that contain details for any entities that were not successfully added, updated, or deleted.
Take the AddKeywords operation for example, I am also trying to build a centralized the way to handle all kinds of errors or exceptions populated by Bing or the application itself.

 @Override
 public boolean addKeywords(Long adGroupId, List<BiddableKeyword> adGroupKeywords) {
  logger.debug("Adding {} keywords of ad group {}", adGroupKeywords.size(), adGroupId);
  boolean isAllGood = true;
  AddKeywordsRequest request = new AddKeywordsRequest();
  request.setAdGroupId(adGroupId);
  com.microsoft.bingads.CampaignManagement.v9.Keyword[] keywords = new com.microsoft.bingads.CampaignManagement.v9.Keyword[adGroupKeywords
    .size()];
  for (int i = 0; i < adGroupKeywords.size(); i++) {
   BiddableKeyword keyword = adGroupKeywords.get(i);
   keywords[i] = new com.microsoft.bingads.CampaignManagement.v9.Keyword();
   //keywords[i].setId(keyword.getApiId());
   keywords[i].setText(keyword.getKeywordText());
   keywords[i].setMatchType(getMatchType(keyword));
   if (keyword.getUserStatus() != null)
    keywords[i].setStatus(getUserStatus(keyword));
   if (keyword.getMaxCpc() != null)
    keywords[i].setBid(new Bid(keyword.getMaxCpc() == 0.0 ? null : keyword.getMaxCpc()));
   if (keyword.getDestinationUrl() != null)
    keywords[i].setDestinationUrl(keyword.getDestinationUrl());
   if (keyword.getAdParam1() != null)
    keywords[i].setParam1(keyword.getAdParam1());
   if (keyword.getAdParam2() != null)
    keywords[i].setParam2(keyword.getAdParam2());
   if (keyword.getAdParam3() != null)
    keywords[i].setParam2(keyword.getAdParam3());

  }
  request.setKeywords(keywords);
  // submit changes now!
  int triedTimes = 0;
  while (triedTimes < Constants.API_CALL_TRY_TIMES) {
   try {
    AddKeywordsResponse response = serviceStub.addKeywords(request);
    populateBatchErrors(adGroupKeywords, response.getPartialErrors());
    assignedReturnedKeywordIds(adGroupKeywords, response);
    isAllGood = response != null && isAllGood;
    return isAllGood;
   } catch (RemoteException exception) {
    triedTimes++;
    if (handleApiCallException(adGroupKeywords, exception, triedTimes))
     break;
   }
  }
  return isAllGood;
 }
Bing Ads V9 put the BatchError and ApiFaultDetail in different packages instead of a unified error package which makes thing awkward. so you have to define the populateBatchErrors() for each BatchError type and handleApiFaultDetail() for each ApiFaultDetail:

com.microsoft.bingads.CampaignManagement.v9.BatchError
com.microsoft.bingads.Reporting.v9.BatchError
com.microsoft.bingads.Optimizer.v9.BatchError
com.microsoft.bingads.AdIntelligence.v9.BatchError
 protected void populateBatchErrors(List<? extends ApiEntity> apiEntities, com.microsoft.bingads.CampaignManagement.v9.BatchError[] batchErrors) {
  for (com.microsoft.bingads.CampaignManagement.v9.BatchError batchError : batchErrors) {
   ApiEntity apiEntity = apiEntities.get(batchError.getIndex());
   if (apiEntity.getApiError() == null) {
    apiEntity.setApiError(new ApiError());
    ApiError apiError = apiEntity.getApiError();
    apiError.setItemIndex(batchError.getIndex());
    apiError.appendErrorString(batchError.getMessage());
    apiError.appendErrorType(batchError.getErrorCode());
    apiError.appendErrorString(batchError.getDetails());
   }
  }
 }
 // return true to break the loop
 protected boolean handleApiCallException(List<? extends ApiEntity> apiEntities, Exception exception, int triedTimes) {
  logger.debug("Got {} exception, tried {} times.", exception.getClass().getSimpleName(), triedTimes);
  if (exception instanceof EditorialApiFaultDetail) {
   // not neccessary to retry
   handleEditorialApiFaultDetail(apiEntities, (EditorialApiFaultDetail) exception);
   return true; // not neccessary to retry!
  } else if (exception instanceof AdApiFaultDetail) {
   handleAdApiFaultDetail(apiEntities, (AdApiFaultDetail) exception);
   return true; // not neccessary to retry!
  } else if (exception instanceof com.microsoft.bingads.CampaignManagement.v9.ApiFaultDetail) {
   handleApiFaultDetail(apiEntities, (com.microsoft.bingads.CampaignManagement.v9.ApiFaultDetail) exception);
   return true; // not neccessary to retry!
  } else if (exception instanceof com.microsoft.bingads.Reporting.v9.ApiFaultDetail) {
   handleApiFaultDetail(apiEntities, (com.microsoft.bingads.Reporting.v9.ApiFaultDetail) exception);
   return true; // not neccessary to retry!
  } else if (exception instanceof com.microsoft.bingads.Optimizer.v9.ApiFaultDetail) {
   handleApiFaultDetail(apiEntities, (com.microsoft.bingads.Optimizer.v9.ApiFaultDetail) exception);
   return true; // not neccessary to retry!
  } else if (exception instanceof com.microsoft.bingads.AdIntelligence.v9.ApiFaultDetail) {
   handleApiFaultDetail(apiEntities, (com.microsoft.bingads.AdIntelligence.v9.ApiFaultDetail) exception);
   return true; // not neccessary to retry!   
  } else {
   if (triedTimes == Constants.API_CALL_TRY_TIMES)
    handleDefaultException(apiEntities, exception);
  }
  return false;
 }

 protected void handleEditorialApiFaultDetail(List<? extends ApiEntity> apiEntities, EditorialApiFaultDetail faultDetail) {
  for (EditorialError editorialError : faultDetail.getEditorialErrors()) {
   ApiEntity apiEntity = apiEntities.get(editorialError.getIndex());
   // Seems the same index might occur more than once due to the different publisher countries, so only use the first one.
   if (apiEntity.getApiError() == null) {
    apiEntity.setApiError(new ApiError());
    ApiError apiError = apiEntity.getApiError();
    apiError.setItemIndex(editorialError.getIndex());
    apiError.appendErrorString(editorialError.getMessage());
    apiError.appendErrorType(editorialError.getErrorCode());
    if (editorialError.getDisapprovedText() != null && editorialError.getDisapprovedText().length() > 0) {
     apiError.appendErrorString(" [" + editorialError.getDisapprovedText() + "]");
    }
   }
  }
  for (com.microsoft.bingads.CampaignManagement.v9.OperationError operationError : faultDetail.getOperationErrors()) {
   for (ApiEntity apiEntity : apiEntities) {
    if (apiEntity.getApiError() == null)
     apiEntity.setApiError(new ApiError());
    ApiError apiError = apiEntity.getApiError();
    apiError.setItemIndex(ApiError.ALL_FAILED_INDEX);
    apiError.appendErrorString(operationError.getMessage());
    apiError.appendErrorType(operationError.getErrorCode());
    apiError.appendErrorString(operationError.getDetails());
   }
  }
  populateBatchErrors(apiEntities, faultDetail.getBatchErrors());
 }

 protected void handleAdApiFaultDetail(List<? extends ApiEntity> apiEntities, AdApiFaultDetail faultDetail) {
  for (ApiEntity apiEntity : apiEntities) {
   if (apiEntity.getApiError() == null)
    apiEntity.setApiError(new ApiError());
   ApiError apiError = apiEntity.getApiError();
   for (AdApiError error : faultDetail.getErrors()) {
    apiError.appendErrorString(error.getMessage());
    apiError.appendErrorType(error.getErrorCode());
    apiError.appendErrorString(error.getDetail());
   }
  }
 }