acquia_commercemanager-8.x-1.122/modules/acm/src/Connector/AgentRequestTrait.php
modules/acm/src/Connector/AgentRequestTrait.php
<?php
namespace Drupal\acm\Connector;
use Acquia\Hmac\Exception\MalformedResponseException;
use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\TransferStats;
use GuzzleHttp\Exception\RequestException;
/**
* Trait AgentRequestTrait.
*
* @package Drupal\acm\Connector
*
* @ingroup acm
*/
trait AgentRequestTrait {
/**
* Version of API.
*
* @var string
*/
protected $apiVersion;
/**
* HTTP (Guzzle) Connector Client Factory.
*
* @var ClientFactory
*/
protected $clientFactory;
/**
* Debug / Verbose Connection Logging.
*
* @var bool
*/
private $debug;
/**
* System / Watchdog Logger.
*
* @var \Psr\Log\LoggerInterface
*/
protected $logger;
/**
* TryAgentRequest.
*
* Try a simple request with the Guzzle client, adding debug callbacks
* and catching / logging request exceptions if needed.
*
* @param callable $doReq
* Request closure, passed client and opts array.
* @param string $action
* Action name for logging.
* @param string $reskey
* Result data key (or NULL)
* @param string $acm_uuid
* The acm_uuid used to create the client.
*
* @return mixed
* API response.
*
* @throws \Exception
*/
protected function tryAgentRequest(callable $doReq, $action, $reskey = NULL, $acm_uuid = "") {
$client = $this->clientFactory->createClient($acm_uuid);
$reqOpts = [];
$logger = ($this->logger) ?: \Drupal::logger('acm');
if ($this->debug) {
$logger->info(sprintf('%s: Attempting Request.', $action));
// Log transfer final endpoint and total time in debug mode.
$reqOpts['on_stats'] =
function (TransferStats $stats) use ($logger, $action) {
$code =
($stats->hasResponse()) ?
$stats->getResponse()->getStatusCode() :
0;
$logger->info(sprintf(
'%s: Requested %s in %.4f [%d].',
$action,
$stats->getEffectiveUri(),
$stats->getTransferTime(),
$code
));
};
}
if ($acm_uuid) {
// This can be overridden in doReq function or using updateStoreContext.
$reqOpts['query']['store_id'] = $acm_uuid;
}
else {
// This can be overridden in doReq function or using updateStoreContext.
$reqOpts['query']['store_id'] = $this->storeId;
}
// Make Request.
try {
/** @var \GuzzleHttp\Psr7\Response $result */
$result = $doReq($client, $reqOpts);
}
catch (\Exception $e) {
$class = get_class($e);
$mesg = sprintf(
'%s: %s during request: (%d) - %s',
$action,
$class,
$e->getCode(),
$e->getMessage()
);
$logger->error($mesg);
// REDUNDANT at 20180531 because now we set http_errors = false.
if ($e->getCode() == 404
|| $e instanceof MalformedResponseException
|| $e instanceof ConnectException) {
throw new \Exception(acm_api_down_global_error_message(), APIWrapper::API_DOWN_ERROR_CODE);
}
elseif ($e instanceof RequestException) {
throw new ConnectorException($mesg, $e->getCode(), $e);
}
else {
throw $e;
}
}
// This code means we must always return valid JSON for every HTTP status.
// Is that what we want to enforce? Probably yes.
$response = json_decode($result->getBody(), TRUE);
if (($response === NULL) || ($this->apiVersion === 'v1' && !isset($response['success']))) {
$mesg = sprintf(
'%s: Invalid / Unrecognized Connector response: %s',
$action,
$result->getBody()
);
$logger->error($mesg);
throw new ConnectorException($mesg);
}
// Earlier we set http_errors = false during client-creation so now
// we need to handle all response statuses here.
// For now (at 20180531) we simply handle http 500 'customer not found'
// And revert to the previous behaviour for all other non-200 responses.
$exception = NULL;
switch ($result->getStatusCode()) {
case 200:
// Continue.
break;
case 500:
if (array_key_exists('code', $response)) {
if ($response['code'] == CustomerNotFoundException::CUSTOMER_NOT_FOUND_CODE) {
// Are we logging here? CustomerNotFound is routine so
// we choose not to log this exception.
$exception = new CustomerNotFoundException($response['message'], $response['code']);
}
else {
$exception = new ConnectorException($response['message'], $response['code']);
}
}
else {
$exception = new ConnectorException($result->getBody(), $result->getStatusCode());
}
break;
default:
throw new ConnectorException($result->getBody(), $result->getStatusCode());
}
if ($exception) {
throw $exception;
}
if ($this->apiVersion === 'v1' && !$response['success']) {
$logger->info(sprintf(
'%s: Connector request unsuccessful: %s',
$action,
$result->getBody()
));
// Process the response to check if error is downtime error
// from Magento.
$errors = [];
if (preg_match('/response:(.*)/i', $result->getBody(), $errors)) {
if (isset($errors[1])) {
$error = json_decode(strtolower($errors[1]), TRUE);
if (isset($error['status'])) {
$error_code = (int) $error['status'];
if ($error_code >= 500 && $error_code < 600) {
throw new \Exception(acm_api_down_global_error_message(), APIWrapper::API_DOWN_ERROR_CODE);
}
}
}
}
throw new ConnectorResultException($response);
}
if ($this->apiVersion === 'v1' && strlen($reskey)) {
if (!isset($response[$reskey])) {
throw new ConnectorResultException($response);
}
return ($response[$reskey]);
}
else {
if ($this->debug) {
$logger->debug("Response: " . nl2br(print_r($response, TRUE)));
}
return ($response);
}
}
}
