View source
<?php
namespace Drupal\acquia_search;
use Drupal\Component\Serialization\Json;
use Drupal\Component\Utility\Crypt;
use Drupal\Core\Cache\CacheBackendInterface;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
class AcquiaSearchApiClient {
protected $authInfo;
protected $cache;
protected $client;
protected $headers = [
'Content-Type' => 'application/json',
'Accept' => 'application/json',
];
public function __construct(array $auth_info, Client $http_client, CacheBackendInterface $cache) {
$this->authInfo = $auth_info;
$this->client = $http_client;
$this->cache = $cache;
}
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)) {
$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,
];
}
$this->cache
->set($cid, $result, $now + 24 * 60 * 60);
return $result;
}
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;
}
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 . '"';
}
private function calculateAuthString() {
$nonce = Crypt::randomBytesBase64(24);
return 'id=' . $this->authInfo['app_uuid'] . '&nonce=' . $nonce . '&realm=search&version=2.0';
}
}