/**
  * This method is a slightly modified version of the one in the parent API Client class. Rather
  * that taking in an API token and performing requests this method takes in an API key which is
  * then used to retrieve an API token from the API (authenticate) and then makes requests to the
  * client reference being decorated.
  *
  * @param  string  $key       The API key to be used to authenticate with the API.
  * @param  string  $resource  The API endpoint the request should be made to.
  * @param  string  $method    The HTTP verb that should be used to make the request.
  * @param  array   $data      Any data that should be passed along with the request.
  * @param  array   $headers   The HTTP headers to be sent with the request.
  * @param  array   $options   Extra options that may be passed into the request. This argument
  *                            mostly exists to facilitate the decorators. Possible keys used by
  *                            this decorator include 'reAuth' and 'attempts'
  *
  * @return array              The API response structure.
  *
  */
 public function sendRequest($key, $resource, $method = "GET", array $data = array(), array $headers = array(), array $options = array())
 {
     // Apply some default values for options that we may not get.
     $options['reAuth'] = array_key_exists('reAuth', $options) ? $options['reAuth'] : false;
     $options['attempts'] = array_key_exists('attempts', $options) ? $options['attempts'] : 1;
     // Get an array of the arguments to this function call.
     $args = func_get_args();
     // Retrieve authentication information.
     $auth = $this->authenticate($key, array(), array('force' => $options['reAuth']));
     $token = $auth['responseData']['data']['api_token'];
     /* Since we will be passing the token instead of key we want to add the key to the options
        so that we may refer to it later (hint: in the caching decorator) */
     $options['key'] = $key;
     try {
         // Forward the request on to the API Client.
         $result = $this->client->sendRequest($token, $resource, $method, $data, $headers, $options);
     } catch (Wolfnet_Api_ApiException $e) {
         $e->append('Several attempts were made.');
         $e->append($e->getCode() === Wolfnet_Api_Client::AUTH_ERROR ? 'auth-code' : '!auth-code');
         $e->append($options['attempts'] < 5 ? 'attempts' : '!attempts');
         if ($e->getCode() === Wolfnet_Api_Client::AUTH_ERROR && $options['attempts'] < 5) {
             $options['reAuth'] = true;
             // This will force the authentication to bypass caching
             $options['attempts']++;
             $result = $this->sendRequest($key, $resource, $method, $data, $headers, $options);
         } elseif ($options['attempts'] > 1) {
             throw $e->append('Several attempts were made.');
         } else {
             throw $e;
         }
     }
     return $result;
 }
 /**
  * This method will first attempt to retrieve the request from the cache when appropriate. If
  * no value is found in the cache it will defer the request to the API client being decorated
  * and will then cache the response if appropriate.
  *
  * NOTE: Only GET requests should be cached per HTTP specs
  *
  * @param  string  $token     The API token to be used to make the request.
  * @param  string  $resource  The API endpoint the request should be made to.
  * @param  string  $method    The HTTP verb that should be used to make the request.
  * @param  array   $data      Any data that should be passed along with the request.
  * @param  array   $headers   The HTTP headers to be sent with the request.
  * @param  array   $options   Extra options that may be passed into the request. This argument
  *                            mostly exists to facilitate the decorators. Possible keys used by
  *                            this decorator include 'key' and 'force'.
  *
  * @return array              The API response structure.
  *
  */
 public function sendRequest($token, $resource, $method = "GET", array $data = array(), array $headers = array(), array $options = array())
 {
     $args = func_get_args();
     $result = null;
     /* Attempt to retrieve a 'key' value from the options argument which will be use to uniquely
      * identify requests being made for that specific key. If none is present we fall back to
      * the token which is even more unique. */
     $options['key'] = array_key_exists('key', $options) ? $options['key'] : $token;
     /* Attempt to retrieve the 'cache' value from the options argument. */
     $options['cache'] = array_key_exists('cache', $options) ? $options['cache'] : true;
     /* If the force key is present we should force the decorator to retrieve new data from the
      * API even if cached data was found. */
     $options['force'] = array_key_exists('force', $options) ? $options['force'] : false;
     // The request is not a GET request we should not be caching.
     if ($method != 'GET') {
         $options['cache'] = false;
     }
     // Generate a cache key if appropriate
     $cacheKey = $options['cache'] ? $this->cacheKeyFromRequest($options['key'], $resource, $data) : null;
     // Attempt to use the key to retrieve data.
     if ($cacheKey !== null) {
         $result = $this->service->cacheGet($cacheKey);
     }
     // If we don't have any data yet perform the request.
     if ($options['force'] || $result === null) {
         try {
             $result = $this->client->sendRequest($token, $resource, $method, $data, $headers, $options);
         } catch (Wolfnet_Api_ApiException $e) {
             /* If the exception states that the API Token is invalid we should remove it from
                the cache so that we retrieve a new one on the next request. */
             if ($e->getCode() === Wolfnet_Api_Client::AUTH_ERROR) {
                 $this->service->cacheDelete($this->cacheKeyFromApiKey($options['key']));
             }
             throw $e;
         }
         // Now that we have the data set it in the cache if we have a key.
         if ($cacheKey !== null) {
             $this->service->cachePut($cacheKey, $result, self::CACHE_SPAN);
         }
     } else {
         $result['fromCache'] = true;
     }
     $result['cacheKey'] = $cacheKey;
     return $result;
 }