class AcquiaSearchApiClient in Acquia Search 3.x
Acquia implementation of the Search API Client.
@package Drupal\acquia_search
Hierarchy
- class \Drupal\acquia_search\AcquiaSearchApiClient
Expanded class hierarchy of AcquiaSearchApiClient
2 files declare their use of AcquiaSearchApiClient
- AcquiaSearchApiClientTest.php in tests/
src/ Unit/ AcquiaSearchApiClientTest.php - Runtime.php in src/
Helper/ Runtime.php
File
- src/
AcquiaSearchApiClient.php, line 16
Namespace
Drupal\acquia_searchView source
class AcquiaSearchApiClient {
/**
* Authentication array.
*
* @var array
*/
protected $authInfo;
/**
* Cache backend.
*
* @var \Drupal\Core\Cache\CacheBackendInterface
*/
protected $cache;
/**
* The HTTP client to fetch the feed data with.
*
* @var \GuzzleHttp\Client
*/
protected $client;
/**
* HTTP headers.
*
* @var array
*/
protected $headers = [
'Content-Type' => 'application/json',
'Accept' => 'application/json',
];
/**
* AcquiaSearchApiClient constructor.
*
* @param array $auth_info
* Authorization array.
* @param \GuzzleHttp\Client $http_client
* HTTP client.
* @param \Drupal\Core\Cache\CacheBackendInterface $cache
* Cache backend.
*/
public function __construct(array $auth_info, Client $http_client, CacheBackendInterface $cache) {
$this->authInfo = $auth_info;
$this->client = $http_client;
$this->cache = $cache;
}
/**
* Helper function to fetch all search v3 indexes for given subscription.
*
* @param string $id
* Acquia Subscription Identifier.
*
* @return array|bool
* Acquia Search indexes array, FALSE on Acquia Search API failure.
*/
public function getSearchIndexes(string $id) {
if (empty($id)) {
return FALSE;
}
$cid = 'acquia_search.indexes.' . $id;
$now = \Drupal::time()
->getRequestTime();
if (($cache = $this->cache
->get($cid)) && $cache->expire > $now) {
return $cache->data;
}
$path = '/v2/index/configure';
$query_string = 'network_id=' . $id;
$result = [];
$indexes = $this
->searchRequest($path, $query_string);
if (empty($indexes) && !is_array($indexes)) {
// When API is not reachable, cache it for 1 minute.
$this->cache
->set($cid, FALSE, \Drupal::time()
->getRequestTime() + 60);
return FALSE;
}
foreach ($indexes as $index) {
$result[$index['key']] = [
'balancer' => $index['host'],
'core_id' => $index['key'],
'data' => $index,
];
}
// Cache will be set in both cases, 1. when search v3 cores are found and
// 2. when there are no search v3 cores but api is reachable.
$this->cache
->set($cid, $result, $now + 24 * 60 * 60);
return $result;
}
/**
* Create and send a request to search controller.
*
* @param string $path
* Path to call.
* @param string $query_string
* Query string to call.
*
* @return array|false
* Response array or FALSE.
*/
public function searchRequest(string $path, string $query_string) {
$host = $this->authInfo['host'];
$req_time = \Drupal::time()
->getRequestTime();
$authorization_string = $this
->calculateAuthString();
$req_params = [
'GET',
preg_replace('/^https?:\\/\\//', '', $host),
$path,
$query_string,
$authorization_string,
$req_time,
];
$authorization_header = $this
->calculateAuthHeader($req_params, $authorization_string);
$data = [
'host' => $host,
'headers' => [
'Authorization' => $authorization_header,
'X-Authorization-Timestamp' => $req_time,
],
];
$uri = $data['host'] . $path . '?' . $query_string;
$options = [
'headers' => $data['headers'],
'timeout' => 5,
];
try {
$response = $this->client
->get($uri, $options);
if (!$response) {
throw new \Exception('Empty Response');
}
$stream_size = $response
->getBody()
->getSize();
$data = Json::decode($response
->getBody()
->read($stream_size));
$status_code = $response
->getStatusCode();
if ($status_code < 200 || $status_code > 299) {
\Drupal::logger('acquia_search')
->error("Couldn't connect to search v3 API: @message", [
'@message' => $response
->getReasonPhrase(),
]);
return FALSE;
}
return $data;
} catch (RequestException $e) {
if ($e
->getCode() == 401) {
\Drupal::logger('acquia_search')
->error("Couldn't connect to search v3 API:\n Received a 401 response from the API. @message", [
'@message' => $e
->getMessage(),
]);
}
elseif ($e
->getCode() == 404) {
\Drupal::logger('acquia_search')
->error("Couldn't connect to search v3 API:\n Received a 404 response from the API. @message", [
'@message' => $e
->getMessage(),
]);
}
else {
\Drupal::logger('acquia_search')
->error("Couldn't connect to search v3 API:\n @message", [
'@message' => $e
->getMessage(),
]);
}
} catch (\Exception $e) {
\Drupal::logger('acquia_search')
->error("Couldn't connect to search v3 API: @message", [
'@message' => $e
->getMessage(),
]);
}
return FALSE;
}
/**
* Creates an authenticator based on a HMAC V2 signer.
*
* @param array $req_params
* Request parameters.
* @param string $authorization_string
* Authorization string.
*
* @return string
* Returns the signed auth header
*/
private function calculateAuthHeader(array $req_params, string $authorization_string) : string {
$signature_base_string = implode("\n", $req_params);
$digest = hash_hmac('sha256', $signature_base_string, base64_decode($this->authInfo['key'], TRUE), TRUE);
$signature = base64_encode($digest);
$authorization_header_string = str_replace("=", "=\"", str_replace("&", "\",", $authorization_string));
return 'acquia-http-hmac ' . $authorization_header_string . '",signature="' . $signature . '"';
}
/**
* Calculates authorization string.
*
* @return string
* Returns the auth string.
*/
private function calculateAuthString() {
$nonce = Crypt::randomBytesBase64(24);
return 'id=' . $this->authInfo['app_uuid'] . '&nonce=' . $nonce . '&realm=search&version=2.0';
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
AcquiaSearchApiClient:: |
protected | property | Authentication array. | |
AcquiaSearchApiClient:: |
protected | property | Cache backend. | |
AcquiaSearchApiClient:: |
protected | property | The HTTP client to fetch the feed data with. | |
AcquiaSearchApiClient:: |
protected | property | HTTP headers. | |
AcquiaSearchApiClient:: |
private | function | Creates an authenticator based on a HMAC V2 signer. | |
AcquiaSearchApiClient:: |
private | function | Calculates authorization string. | |
AcquiaSearchApiClient:: |
public | function | Helper function to fetch all search v3 indexes for given subscription. | |
AcquiaSearchApiClient:: |
public | function | Create and send a request to search controller. | |
AcquiaSearchApiClient:: |
public | function | AcquiaSearchApiClient constructor. |