/**
  * Make the HTTP call (Sync)
  * @param string $resourcePath path to method endpoint
  * @param string $method       method to call
  * @param array  $queryParams  parameters to be place in query URL
  * @param array  $postData     parameters to be placed in POST body
  * @param array  $headerParams parameters to be place in request header
  * @throws ServiceException on a non 2xx response
  * @return mixed
  */
 public function callApi($resourcePath, $method, $queryParams, $postData, $headerParams)
 {
     $headers = array();
     $headers[] = "Accept: application/json";
     $headers[] = "Content-Type: application/json";
     if (!empty($headerParams)) {
         foreach ($headerParams as $key => $val) {
             $headers[] = "{$key}: {$val}";
         }
     }
     if ($postData && is_object($postData) or is_array($postData)) {
         // json model
         $postData = json_encode($postData);
     }
     $url = $this->config->getBaseUrl() . $resourcePath;
     $curl = curl_init();
     // set timeout, if needed
     if ($this->config->getCurlTimeout() != 0) {
         curl_setopt($curl, CURLOPT_TIMEOUT, $this->config->getCurlTimeout());
     }
     // return the result on success, rather than just TRUE
     curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
     curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
     // disable SSL verification, if needed
     if ($this->config->getSSLVerification() == false) {
         curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
         curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0);
     }
     if (is_null($queryParams)) {
         $queryParams = array();
     }
     $queryParams['apiKey'] = $this->config->getApiKey();
     $url = $url . '?' . http_build_query($queryParams);
     if ($method == self::$POST) {
         curl_setopt($curl, CURLOPT_POST, true);
         curl_setopt($curl, CURLOPT_POSTFIELDS, $postData);
     } else {
         if ($method == self::$HEAD) {
             curl_setopt($curl, CURLOPT_NOBODY, true);
         } else {
             if ($method == self::$OPTIONS) {
                 curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "OPTIONS");
                 curl_setopt($curl, CURLOPT_POSTFIELDS, $postData);
             } else {
                 if ($method == self::$PATCH) {
                     curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "PATCH");
                     curl_setopt($curl, CURLOPT_POSTFIELDS, $postData);
                 } else {
                     if ($method == self::$PUT) {
                         curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "PUT");
                         curl_setopt($curl, CURLOPT_POSTFIELDS, $postData);
                     } else {
                         if ($method == self::$DELETE) {
                             curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "DELETE");
                             curl_setopt($curl, CURLOPT_POSTFIELDS, $postData);
                         } else {
                             if ($method != self::$GET) {
                                 $faultInfo = new FaultInfo('BadRequest', new Blame('CLIENT'), 'Method ' . $method . ' is not recognized.', 1100, null, Retry::no(), Retry::no());
                                 throw new ServiceException($faultInfo->getMessage(), $faultInfo, null);
                             }
                         }
                     }
                 }
             }
         }
     }
     if ($this->config->getDebug()) {
         error_log("[DEBUG] URL is {$url}", 3, $this->config->getDebugFile());
     }
     curl_setopt($curl, CURLOPT_URL, $url);
     // Set user agent
     curl_setopt($curl, CURLOPT_USERAGENT, $this->config->getUserAgent());
     // debugging for curl
     if ($this->config->getDebug()) {
         error_log("[DEBUG] HTTP Request body  ~BEGIN~\n" . print_r($postData, true) . "\n~END~\n", 3, $this->config->getDebugFile());
         curl_setopt($curl, CURLOPT_VERBOSE, 1);
         //this doesn't work, i get:
         //"curl_setopt(): cannot represent a stream of type Output as a STDIO FILE*"
         //            curl_setopt($curl, CURLOPT_STDERR, fopen($this->config->getDebugFile(), 'a'));
     } else {
         curl_setopt($curl, CURLOPT_VERBOSE, 0);
     }
     // obtain the HTTP response headers
     curl_setopt($curl, CURLOPT_HEADER, 1);
     // Make the request
     $response = curl_exec($curl);
     $http_header_size = curl_getinfo($curl, CURLINFO_HEADER_SIZE);
     $http_header = substr($response, 0, $http_header_size);
     $http_body = substr($response, $http_header_size);
     $response_info = curl_getinfo($curl);
     $httpResponseData = new HttpResponseData($url, $response_info['http_code'], $http_body, $http_header);
     // debug HTTP response body
     if ($this->config->getDebug()) {
         error_log("[DEBUG] HTTP Response body ~BEGIN~\n" . print_r($http_body, true) . "\n~END~\n", 3, $this->config->getDebugFile());
     }
     // Handle the response
     if ($response_info['http_code'] == 0) {
         $faultInfo = new FaultInfo('NetworkTimeout', new Blame('SERVER'), "API call to {$url} timed out: " . serialize($response_info), null, null, Retry::no(), Retry::no());
         throw new ServiceException($faultInfo->getMessage(), $faultInfo, $httpResponseData);
     } else {
         if ($response_info['http_code'] >= 200 && $response_info['http_code'] <= 299) {
             $data = json_decode($http_body);
             if (json_last_error() > 0) {
                 // if response is a string
                 $data = $http_body;
             }
         } else {
             $data = json_decode($http_body);
             if (json_last_error() > 0) {
                 // if response is a string
                 $data = $http_body;
             }
             $faultInfo = null;
             $msg = null;
             try {
                 $faultInfo = FaultInfoUnmarshaller::unmarshallJsonObject($data);
                 $msg = $faultInfo->getMessage();
             } catch (\Exception $e) {
                 //TODO log.
                 //I'm not throwing this because it would hide the original error.
                 $msg = $e->getMessage();
             }
             throw new ServiceException($msg, $faultInfo, $httpResponseData);
         }
     }
     return array($data, $httpResponseData);
 }
 /**
  * @param $response
  * @param $httpResponseData
  * @return ServiceException
  */
 protected function unmarshallingFailed($response, $httpResponseData)
 {
     $faultInfo = new FaultInfo('BadRequest', new Blame('CLIENT'), 'Server sent unexpected or unsupported response!', 1100, null, Retry::no(), Retry::no());
     return new ServiceException($faultInfo->getMessage(), $faultInfo, $httpResponseData);
 }