DataApi.php in Acquia Lift Connector 8
Contains \Drupal\acquia_lift\Service\Api\DataApi.
Namespace
Drupal\acquia_lift\Service\ApiFile
src/Service/Api/DataApi.phpView source
<?php
/**
* @file
* Contains \Drupal\acquia_lift\Service\Api\DataApi.
*/
namespace Drupal\acquia_lift\Service\Api;
use Drupal\acquia_lift\Exception\DataApiCredentialException;
use Drupal\acquia_lift\Exception\DataApiException;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Routing\RequestContext;
use Drupal\acquia_lift\Service\Helper\SettingsHelper;
use GuzzleHttp\ClientInterface;
use GuzzleHttp\Psr7\Request;
class DataApi implements DataApiInterface {
/**
* An http client for making calls to Acquia Lift web data collection service.
*
* @var \GuzzleHttp\ClientInterface
*/
private $httpClient;
/**
* The current Drupal request.
*
* @var \Drupal\Core\Routing\RequestContext
*/
private $context;
/**
* The logger to use for errors and notices.
*
* @var \Psr\Log\LoggerInterface;
*/
private $logger;
/**
* Acquia Lift credential settings.
*
* @var array
*/
private $credentialSettings;
/**
* The list of headers that can be used in the canonical request.
*
* @var array
*/
private $headerWhitelist = [
'Accept',
'Host',
'User-Agent',
];
/**
* Constructor.
*
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The config factory service.
* @param \GuzzleHttp\ClientInterface $http_client
* A Guzzle client interface.
* @param \Drupal\Core\Routing\RequestContext $context
* The current request.
*
* @throws \Drupal\acquia_lift\Exception\DataApiCredentialException
*/
public function __construct(ConfigFactoryInterface $config_factory, ClientInterface $http_client, RequestContext $context) {
$this->httpClient = $http_client;
$this->context = $context;
$this->logger = \Drupal::logger('acquia_lift');
$this->credentialSettings = $config_factory
->get('acquia_lift.settings')
->get('credential');
if (SettingsHelper::isInvalidCredential($this->credentialSettings)) {
throw new DataApiCredentialException('Acquia Lift credential is invalid.');
}
}
/**
* Resets the singleton instance.
*
* Used in unit tests.
*/
public static function reset() {
// This needs to be thought through. The D8 service handling ensures that
// this is a singleton. The test instance should be created via
// factory and that means that this functionality is likely moved outside of
// this class.
throw new \Exception('Todo: reset API not yet implemented');
}
/**
* Generates an endpoint for a particular section of the Acquia Lift Data API.
*
* @param string $path
* The endpoint path, e.g. 'segments' or 'events/my-event'
* @return string
* The endpoint to make calls to.
*/
private function generateEndpoint($path) {
$url_scheme = $this->context
->getScheme() == 'https' ? 'https://' : 'http://';
return $url_scheme . $this->credentialSettings['api_url'] . '/dashboard/rest/' . $this->credentialSettings['account_name'] . '/' . $path;
}
/**
* 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.
*
* @return string
* The canonical representation of the request.
*/
private function canonicalizeRequest($method, $url, $parameters = [], $headers = []) {
$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 (!isset($headers['User-Agent'])) {
$client_config = $this->httpClient
->getConfig();
$headers['User-Agent'] = $client_config['headers']['User-Agent'];
}
if (!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
*/
private function getAuthHeader($method, $path, $parameters = [], $headers = []) {
$canonical = $this
->canonicalizeRequest($method, $path, $parameters, $headers);
$hmac = base64_encode(hash_hmac('sha1', (string) $canonical, $this->credentialSettings['secret_key'], TRUE));
return 'HMAC ' . $this->credentialSettings['access_key'] . ':' . $hmac;
}
/**
* Fetches the available Segment IDs from Acquia Lift
*
* @return array
* An array of segment IDs that can be used for targeting.
*/
public function getSegments() {
// First get our Authorization header.
$headers = [
'Accept' => 'application/json',
];
$url = $this
->generateEndpoint('segments');
$auth_header = $this
->getAuthHeader('GET', $url, [], $headers);
$headers += [
'Authorization' => $auth_header,
];
$request = new Request('GET', $url, $headers);
$response = $this->httpClient
->send($request);
$data = $response
->getBody();
if (empty($data)) {
return [];
}
$data = json_decode($data, TRUE);
if (is_array($data)) {
$segments = array_values(array_filter($data));
return $segments;
}
return [];
}
/**
* Saves an event to Acquia Lift Data
*
* @param string $event_name
* The 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 DataApiException
*/
public function saveEvent($event_name, $event_type = 'OTHER') {
// First get our Authorization header.
$headers = [
'Accept' => 'application/json',
];
$url = $this
->generateEndpoint('events/' . $event_name);
$auth_header = $this
->getAuthHeader('PUT', $url, [
'type' => $event_type,
], $headers);
$headers += [
'Authorization' => $auth_header,
];
$request = new Request('PUT', $url . '?type=' . $event_type, $headers);
$response = $this->httpClient
->send($request);
$vars = [
'@eventname' => $event_name,
];
$success_msg = SafeMarkup::format('The event @eventname has been saved to Acquia Lift', $vars);
$fail_msg = SafeMarkup::format('Could not save event @eventname to Acquia Lift', $vars);
if ($response
->getStatusCode() != 200) {
$this->logger
->error($fail_msg);
throw new DataApiException($fail_msg);
}
$this->logger
->info($success_msg);
}
/**
* Deletes an event from Acquia Lift Data
*
* @param $event_name
* The name of the event.
*
* @throws DataApiException
*/
public function deleteEvent($event_name) {
// First get our Authorization header.
$url = $this
->generateEndpoint('events/' . $event_name);
$auth_header = $this
->getAuthHeader('DELETE', $url);
$request = new Request('DELETE', $url, [
'Authorization' => $auth_header,
]);
$response = $this->httpClient
->send($request);
$vars = [
'@eventname' => $event_name,
];
$success_msg = SafeMarkup::format('The event @eventname was deleted from Acquia Lift Profiles', $vars);
$fail_msg = SafeMarkup::format('Could not delete event @eventname from Acquia Lift Profiles', $vars);
if ($response
->getStatusCode() != 200) {
$this->logger
->error($fail_msg);
throw new DataApiException($fail_msg);
}
$this->logger
->info($success_msg);
}
}