You are here

class ALProfilesAPI in Acquia Lift Connector 7.2

Same name and namespace in other branches
  1. 7 acquia_lift_profiles/includes/acquia_lift_profiles.classes.inc \ALProfilesAPI

@file Provides an agent type for Acquia Lift Profiles

Hierarchy

Expanded class hierarchy of ALProfilesAPI

File

acquia_lift_profiles/includes/acquia_lift_profiles.classes.inc, line 7
Provides an agent type for Acquia Lift Profiles

View source
class ALProfilesAPI {

  /**
   * An http client for making calls to Acquia Lift Profiles.
   *
   * @var AcquiaLiftDrupalHttpClientInterface
   */
  protected $httpClient;

  /**
   * The API URL for Acquia Lift Profiles.
   *
   * @var string
   */
  protected $apiUrl;

  /**
   * The Acquia Lift Profiles account name to use.
   *
   * @var string
   */
  protected $accountName;

  /**
   * The access key to use for authorization.
   *
   * @var string
   */
  protected $accessKey;

  /**
   * The secret key to use for authorization.
   *
   * @var string
   */
  protected $secretKey;

  /**
   * The list of headers that can be used in the canonical request.
   *
   * @var array
   */
  protected $headerWhitelist = array(
    'Accept',
    'Host',
    'User-Agent',
  );

  /**
   * The logger to use for errors and notices.
   *
   * @var PersonalizeLoggerInterface
   */
  protected $logger = NULL;

  /**
   * The singleton instance.
   *
   * @var ALProfilesAPI
   */
  private static $instance;

  /**
   * Singleton factory method.
   *
   * @param $account_name
   *   The name of the Acquia Lift Profiles account.
   * @param $api_url
   *   The URL to use for API calls.
   * @param $access_key
   *   The access key to use for authorization.
   * @param $secret_key
   *   The secret key to use for authorization.
   *
   * @return ALProfilesAPI
   */
  public static function getInstance($account_name = '', $customer_site = '', $api_url = '', $access_key = '', $secret_key = '') {
    if (empty(self::$instance)) {
      if (drupal_valid_test_ua()) {
        self::setTestInstance();
        return self::$instance;
      }

      // If no account name or api url has been passed in, fallback to getting them
      // from the variables.
      if (empty($account_name)) {
        $account_name = variable_get('acquia_lift_profiles_account_name', '');
      }
      if (empty($customer_site)) {
        $customer_site = variable_get('acquia_lift_profiles_site_name', '');
      }
      if (empty($api_url)) {
        $api_url = variable_get('acquia_lift_profiles_api_url', '');
      }
      if (empty($access_key)) {
        $access_key = variable_get('acquia_lift_profiles_access_key', '');
      }
      if (empty($secret_key)) {
        $secret_key = variable_get('acquia_lift_profiles_secret_key', '');
      }

      // If either account name or API URL is still missing, bail.
      if (empty($api_url) || empty($account_name) || empty($access_key) || empty($secret_key)) {
        throw new ALProfilesCredsException('Missing acquia_lift_profiles account information.');
      }
      if (!valid_url($api_url)) {
        throw new ALProfilesCredsException('API URL is not a valid URL.');
      }
      $needs_scheme = strpos($api_url, '://') === FALSE;
      if ($needs_scheme) {
        global $is_https;

        // Use the same scheme for Acquia Lift Profiles as we are using here.
        $url_scheme = $is_https ? 'https://' : 'http://';
        $api_url = $url_scheme . $api_url;
      }
      if (substr($api_url, -1) === '/') {
        $api_url = substr($api_url, 0, -1);
      }
      self::$instance = new self($account_name, $customer_site, $api_url, $access_key, $secret_key);
    }
    return self::$instance;
  }

  /**
   * Resets the singleton instance.
   *
   * Used in unit tests.
   */
  public static function reset() {
    self::$instance = NULL;
  }

  /**
   * Sets the ALProfilesAPI instance with dummy creds and a dummy HttpClient.
   *
   * This is used during simpletest web tests.
   */
  public static function setTestInstance($broken_http_client = FALSE) {
    module_load_include('inc', 'acquia_lift_profiles', 'tests/acquia_lift_profiles.test_classes');
    self::$instance = new self('TESTACCOUNT', 'TESTSITE', 'http://api.example.com', 'testUser', 'testPass');
    $test_data = variable_get('acquia_lift_profiles_web_test_data', array());
    self::$instance
      ->setHttpClient(new DummyALProfilesHttpClient($broken_http_client, $test_data));
    self::$instance
      ->setLogger(new AcquiaLiftTestLogger(TRUE));
  }

  /**
   * Private constructor as this is a singleton.
   *
   * @param $account_name
   *   The name of the Acquia Lift Profiles account.
   * @param $api_url
   *   The URL to use for API calls.
   * @param $access_key
   *   The access key to use for authorization.
   * @param $secret_key
   *   The secret key to use for authorization.
   */
  private function __construct($account_name, $site, $api_url, $access_key, $secret_key) {
    $this->accountName = $account_name;
    $this->customerSite = $site;
    $this->apiUrl = $api_url;
    $this->accessKey = $access_key;
    $this->secretKey = $secret_key;
  }

  /**
   * Accessor for the accountName property.
   *
   * @return string
   */
  public function getAccountName() {
    return $this->accountName;
  }

  /**
   * Accessor for the customerSite property.
   *
   * @return string
   */
  public function getCustomerSite() {
    return $this->customerSite;
  }

  /**
   * Accessor for the apiUrl property.
   *
   * @return string
   */
  public function getApiUrl() {
    return $this->apiUrl;
  }

  /**
   * Returns an http client to use for Acquia Lift Profiles calls.
   *
   * @return AcquiaLiftDrupalHttpClientInterface
   */
  protected function httpClient() {
    if (!isset($this->httpClient)) {
      $this->httpClient = new AcquiaLiftDrupalHttpClient();
    }
    return $this->httpClient;
  }

  /**
   * Generates an endpoint for a particular section of the Acquia Lift Profiles API.
   *
   * @param string $path
   *   The endpoint path, e.g. 'segments' or 'events/my-event'
   * @return string
   *   The endpoint to make calls to.
   */
  protected function generateEndpoint($path) {
    return $this->apiUrl . '/dashboard/rest/' . $this->accountName . '/' . $path;
  }

  /**
   * Setter for the httpClient property.
   *
   * @param AcquiaLiftDrupalHttpClientInterface $client
   *   The http client to use.
   */
  public function setHttpClient(AcquiaLiftDrupalHttpClientInterface $client) {
    $this->httpClient = $client;
  }

  /**
   * Returns the canonical representation of a request.
   *
   * @param $method
   *   The request method, e.g. 'GET'.
   * @param $path
   *   The path of the request, e.g. '/dashboard/rest/[ACCOUNTNAME]/segments'.
   * @param array $parameters
   *   An array of request parameters.
   * @param array $headers
   *   An array of request headers.
   * @param bool $add_extra_headers
   *   Whether to add the extra headers that we know drupal_http_request will add
   *   to the request. Set to FALSE if the request will not be handled by
   *   drupal_http_request.
   *
   * @return string
   *   The canonical representation of the request.
   */
  public function canonicalizeRequest($method, $url, $parameters = array(), $headers = array(), $add_extra_headers = TRUE) {
    $parsed_url = parse_url($url);
    $str = strtoupper($method) . "\n";

    // Certain headers may get added to the actual request so we need to
    // add them here.
    if ($add_extra_headers && !isset($headers['User-Agent'])) {
      $headers['User-Agent'] = 'Drupal (+http://drupal.org/)';
    }
    if ($add_extra_headers && !isset($headers['Host'])) {
      $headers['Host'] = $parsed_url['host'] . (!empty($parsed_url['port']) ? ':' . $parsed_url['port'] : '');
    }

    // Sort all header names alphabetically.
    $header_names = array_keys($headers);
    uasort($header_names, create_function('$a, $b', 'return strtolower($a) < strtolower($b) ? -1 : 1;'));

    // Add each header (trimmed and lowercased) and value to the string, separated by
    // a colon, and with a new line after each header:value pair.
    foreach ($header_names as $header) {
      if (!in_array($header, $this->headerWhitelist)) {
        continue;
      }
      $str .= trim(strtolower($header)) . ':' . trim($headers[$header]) . "\n";
    }

    // Add the path.
    $str .= $parsed_url['path'];

    // Sort any parameters alphabetically and add them as a querystring to our string.
    if (!empty($parameters)) {
      ksort($parameters);
      $first_param = key($parameters);
      $str .= '?' . $first_param . '=' . array_shift($parameters);
      foreach ($parameters as $key => $value) {
        $str .= '&' . $key . '=' . $value;
      }
    }
    return $str;
  }

  /**
   * Returns a string to use for the 'Authorization' header.
   *
   * @return string
   */
  public function getAuthHeader($method, $path, $parameters = array(), $headers = array()) {
    $canonical = $this
      ->canonicalizeRequest($method, $path, $parameters, $headers, is_a($this
      ->httpClient(), 'AcquiaLiftDrupalHttpClient'));
    $binary = hash_hmac('sha1', (string) $canonical, $this->secretKey, TRUE);
    $hex = hash_hmac('sha1', (string) $canonical, $this->secretKey, FALSE);
    $hmac = base64_encode($binary);
    return 'HMAC ' . $this->accessKey . ':' . $hmac;
  }

  /**
   * Fetches the available Segment IDs from Acquia Lift Profiles
   *
   * @return array
   *   An array of segment IDs that can be used for targeting.
   */
  public function getSegments() {

    // First get our Authorization header.
    $headers = array(
      'Accept' => 'application/json',
    );
    $url = $this
      ->generateEndpoint('segments');
    $params = array();
    if (!empty($this->customerSite)) {
      $params['customerSite'] = $this->customerSite;
    }
    $auth_header = $this
      ->getAuthHeader('GET', $url, $params, $headers);
    $headers += array(
      'Authorization' => $auth_header,
    );
    $querystring = empty($this->customerSite) ? '' : '?customerSite=' . rawurlencode($this->customerSite);
    $response = $this
      ->httpClient()
      ->get($url . $querystring, $headers);
    if ($response->code != 200 || !isset($response->data)) {
      if ($response->code != 200) {
        $vars = array(
          'rest_api_status' => $response->code,
          'rest_api_status_line' => $response->status_message,
        );
        $fail_msg_template = 'Could not get segments from Acquia Lift Profiles "{rest_api_status} {rest_api_status_line}"';
        $this
          ->logger()
          ->log(PersonalizeLogLevel::ERROR, $fail_msg_template, $vars);
        $fail_msg = str_replace(array(
          '{rest_api_status}',
          '{rest_api_status_line}',
        ), array(
          $vars['rest_api_status'],
          $vars['rest_api_status_line'],
        ), $fail_msg_template);
        throw new ALProfilesException($fail_msg);
      }
      return array();
    }
    $data = json_decode($response->data, TRUE);
    if (is_array($data)) {
      $segments = array_values(array_filter($data));
      return $segments;
    }
    return array();
  }

  /**
   * Saves an event to Acquia Lift Profiles
   *
   * @param string $machine_name
   *   The canonical, machine-readable name of the event.
   * @param string $label
   *   The human-readable name of the event.
   * @param string $event_type
   *   The type of event, can be one of 'CAMPAIGN_ACTION', 'CAMPAIGN_CLICK_THROUGH',
   *   'CAMPAIGN_CONVERSION', or 'OTHER' (default).
   *
   * @throws ALProfilesException
   */
  public function saveEvent($event_name, $event_label, $event_type = 'OTHER') {

    // First get our Authorization header.
    $headers = array(
      'Accept' => 'application/json',
    );
    $url = $this
      ->generateEndpoint('events/' . $event_name);
    $params = array(
      'label' => $event_label,
      'type' => $event_type,
    );
    if (!empty($this->customerSite)) {
      $params['customerSite'] = $this->customerSite;
    }
    $auth_header = $this
      ->getAuthHeader('PUT', $url, $params, $headers);
    $headers += array(
      'Authorization' => $auth_header,
    );
    $querystring = empty($this->customerSite) ? '?' : '?customerSite=' . rawurlencode($this->customerSite) . '&';
    $querystring .= 'label=' . rawurlencode($event_label) . '&type=' . $event_type;
    $response = $this
      ->httpClient()
      ->put($url . $querystring, $headers);
    $vars = array(
      'eventname' => $event_name,
    );
    $success_msg = 'The event {eventname} has been saved to Acquia Lift Profiles';
    $fail_msg = 'Could not save event {eventname} to Acquia Lift Profiles';
    $exception_msg = 'Could not save event';
    if ($response->code == 200) {
      $this
        ->logger()
        ->log(PersonalizeLogLevel::INFO, $success_msg, $vars);
    }
    else {
      $this
        ->logger()
        ->log(PersonalizeLogLevel::ERROR, $fail_msg, $vars);
      throw new ALProfilesException($exception_msg);
    }
  }

  /**
   * Deletes an event from Acquia Lift Profiles
   *
   * @param $event_name
   *   The name of the event.
   *
   * @throws ALProfilesException
   */
  public function deleteEvent($event_name) {

    // First get our Authorization header.
    $url = $this
      ->generateEndpoint('events/' . $event_name);
    $params = array();
    $querystring = '';
    if (!empty($this->customerSite)) {
      $params['customerSite'] = $this->customerSite;
      $querystring = '?customerSite=' . $this->customerSite;
    }
    $auth_header = $this
      ->getAuthHeader('DELETE', $url, $params);
    $response = $this
      ->httpClient()
      ->delete($url . $querystring, array(
      'Authorization' => $auth_header,
    ));
    $vars = array(
      'eventname' => $event_name,
    );
    $success_msg = 'The event {eventname} was deleted from Acquia Lift Profiles';
    $fail_msg = 'Could not delete event {eventname} from Acquia Lift Profiles';
    $exception_msg = 'Could not delete event';
    if ($response->code == 200) {
      $this
        ->logger()
        ->log(PersonalizeLogLevel::INFO, $success_msg, $vars);
    }
    else {
      $this
        ->logger()
        ->log(PersonalizeLogLevel::ERROR, $fail_msg, $vars);
      throw new ALProfilesException($exception_msg);
    }
  }

  /**
   * Returns the logger to use.
   *
   * @return PersonalizeLoggerInterface
   */
  protected function logger() {
    if ($this->logger !== NULL) {
      return $this->logger;
    }
    return new PersonalizeLogger();
  }

  /**
   * Implements PersonalizeLoggerAwareInterface::setLogger().
   */
  public function setLogger(PersonalizeLoggerInterface $logger) {
    $this->logger = $logger;
  }

}

Members

Namesort descending Modifiers Type Description Overrides
ALProfilesAPI::$accessKey protected property The access key to use for authorization.
ALProfilesAPI::$accountName protected property The Acquia Lift Profiles account name to use.
ALProfilesAPI::$apiUrl protected property The API URL for Acquia Lift Profiles.
ALProfilesAPI::$headerWhitelist protected property The list of headers that can be used in the canonical request.
ALProfilesAPI::$httpClient protected property An http client for making calls to Acquia Lift Profiles.
ALProfilesAPI::$instance private static property The singleton instance.
ALProfilesAPI::$logger protected property The logger to use for errors and notices.
ALProfilesAPI::$secretKey protected property The secret key to use for authorization.
ALProfilesAPI::canonicalizeRequest public function Returns the canonical representation of a request.
ALProfilesAPI::deleteEvent public function Deletes an event from Acquia Lift Profiles
ALProfilesAPI::generateEndpoint protected function Generates an endpoint for a particular section of the Acquia Lift Profiles API.
ALProfilesAPI::getAccountName public function Accessor for the accountName property.
ALProfilesAPI::getApiUrl public function Accessor for the apiUrl property.
ALProfilesAPI::getAuthHeader public function Returns a string to use for the 'Authorization' header.
ALProfilesAPI::getCustomerSite public function Accessor for the customerSite property.
ALProfilesAPI::getInstance public static function Singleton factory method.
ALProfilesAPI::getSegments public function Fetches the available Segment IDs from Acquia Lift Profiles
ALProfilesAPI::httpClient protected function Returns an http client to use for Acquia Lift Profiles calls.
ALProfilesAPI::logger protected function Returns the logger to use.
ALProfilesAPI::reset public static function Resets the singleton instance.
ALProfilesAPI::saveEvent public function Saves an event to Acquia Lift Profiles
ALProfilesAPI::setHttpClient public function Setter for the httpClient property.
ALProfilesAPI::setLogger public function Implements PersonalizeLoggerAwareInterface::setLogger().
ALProfilesAPI::setTestInstance public static function Sets the ALProfilesAPI instance with dummy creds and a dummy HttpClient.
ALProfilesAPI::__construct private function Private constructor as this is a singleton.