Skip to content

Commit

Permalink
LCH-6356: Adding guzzle 8 compatibility. 2.x. (#166)
Browse files Browse the repository at this point in the history
* LCH-6356: Adding guzzle 8 compatibility.

* LCH-6356: Removing phpstan ignore-error.

* LCH-6356: Removing __call method.

* LCH-6356: Minimum guzzle version to 6.5.

* LCH-6356: Added backward compatibility for guzzle 6 and removed redundant code.

* LCH-6356: Created conditional common trait.

* LCH-6356: Added todo comment.
  • Loading branch information
narendradesai committed Mar 1, 2023
1 parent 66b491f commit f1fecc8
Show file tree
Hide file tree
Showing 5 changed files with 472 additions and 263 deletions.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"ext-json": "*",
"php": ">=7.3",
"psr/log": ">=1.0",
"guzzlehttp/guzzle": ">=6.5 <8.0",
"guzzlehttp/guzzle": ">=6.5",
"symfony/event-dispatcher": ">=4.4",
"symfony/http-foundation": ">=4.4",
"symfony/serializer": ">=4.4"
Expand Down
1 change: 0 additions & 1 deletion phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ parameters:
paths:
- ./src
ignoreErrors:
- '#Class [a-zA-Z0-9\\_:\(\).]* extends [a-zA-Z0-9\\_:@\(\).]*#'
- '#Unsafe usage of new static\(\).#'
excludePaths:
- test/
268 changes: 7 additions & 261 deletions src/ContentHubClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,15 @@
namespace Acquia\ContentHubClient;

use Acquia\ContentHubClient\CDF\CDFObject;
use Acquia\ContentHubClient\Guzzle\Middleware\RequestResponseHandler;
use Acquia\ContentHubClient\SearchCriteria\SearchCriteria;
use Acquia\ContentHubClient\SearchCriteria\SearchCriteriaBuilder;
use Acquia\Hmac\Guzzle\HmacAuthMiddleware;
use GuzzleHttp\Client;
use GuzzleHttp\ClientInterface;
use GuzzleHttp\Exception\BadResponseException;
use GuzzleHttp\Exception\ClientException;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Psr7\Response;
use Psr\Log\LogLevel;
use Symfony\Component\HttpFoundation\Response as HttpResponse;
use Psr\Http\Message\ResponseInterface;
use Psr\Log\LoggerInterface;
Expand All @@ -26,7 +24,9 @@
*
* @package Acquia\ContentHubClient
*/
class ContentHubClient extends Client {
class ContentHubClient implements ClientInterface {

use ContentHubClientTrait;

const LIB_VERSION = '2.x-dev';

Expand Down Expand Up @@ -126,30 +126,11 @@ public function __construct(
$config['handler']->push($middleware);
$this->addRequestResponseHandler($config);

parent::__construct($config);
$this->httpClient = ObjectFactory::getGuzzleClient($config);
$this->setConfigs($config);
}
// phpcs:enable

/**
* Pings the service to ensure that it is available.
*
* @return \Psr\Http\Message\ResponseInterface
* Response.
*
* @throws \GuzzleHttp\Exception\RequestException
* @throws \Exception
*
* @since 0.2.0
*/
public function ping(): ResponseInterface {
$makeBaseURL = self::makeBaseURL($this->getConfig()['base_url']);
$client = ObjectFactory::getGuzzleClient([
'base_uri' => $makeBaseURL,
]);

return $client->get('ping');
}

/**
* Discoverability of the API.
*
Expand Down Expand Up @@ -406,7 +387,7 @@ public function getEntities(array $uuids) {
* @return \Acquia\ContentHubClient\CDF\CDFObjectInterface
* CDFObject
*/
protected function getCDFObject($data) { // phpcs:ignore
public function getCDFObject($data) { // phpcs:ignore
$event = ObjectFactory::getCDFTypeEvent($data);
$this->dispatcher->dispatch($event, ContentHubLibraryEvents::GET_CDF_CLASS);

Expand Down Expand Up @@ -748,18 +729,6 @@ public function getInterestsByWebhook($webhook_uuid) {
return $data['data']['interests'] ?? [];
}

/**
* Get the settings that were used to instantiate this client.
*
* @return \Acquia\ContentHubClient\Settings
* Settings object.
*
* @codeCoverageIgnore
*/
public function getSettings() {
return $this->settings;
}

/**
* Obtains the Settings for the active subscription.
*
Expand Down Expand Up @@ -1107,229 +1076,6 @@ public function removeFilterFromWebhook($filter_id, $webhook_id) {
return self::getResponseJson($response);
}

/**
* Gets a Json Response from a request.
*
* @param \Psr\Http\Message\ResponseInterface $response
* Response.
*
* @return mixed
* Response array.
*
* @throws \Exception
*/
public static function getResponseJson(ResponseInterface $response) {
try {
$body = (string) $response->getBody();
}
catch (\Exception $exception) {
$message = sprintf("An exception occurred in the JSON response. Message: %s",
$exception->getMessage());
throw new \Exception($message);
}

return json_decode($body, TRUE);
}

/**
* {@inheritdoc}
*
* @codeCoverageIgnore
*/
public function __call($method, $args) {
try {
if (strpos($args[0], '?')) {
[$uri, $query] = explode('?', $args[0]);
$parts = explode('/', $uri);
if ($query) {
$last = array_pop($parts);
$last .= "?$query";
$parts[] = $last;
}
}
else {
$parts = explode('/', $args[0]);
}
$args[0] = self::makePath(...$parts);

$args = $this->addSearchCriteriaHeader($args);

return parent::__call($method, $args);
}
catch (\Exception $e) {
return $this->getExceptionResponse($method, $args, $e);
}
}

/**
* Obtains the appropriate exception Response.
*
* Logging error messages according to API call.
*
* @param string $method
* The Request to Plexus, as defined in the content-hub-php library.
* @param array $args
* The Request arguments.
* @param \Exception $exception
* The Exception object.
*
* @return \Psr\Http\Message\ResponseInterface
* The response after raising an exception.
*
* @codeCoverageIgnore
*/
protected function getExceptionResponse($method, array $args, \Exception $exception) {
// If we reach here it is because there was an exception raised in the
// API call.
$api_call = $args[0];
$response = $exception->getResponse();
if (!$response) {
$response = $this->getErrorResponse($exception->getCode(), $exception->getMessage());
}
$response_body = json_decode($response->getBody(), TRUE);
$error_code = $response_body['error']['code'] ?? '';
$error_message = $response_body['error']['message'] ?? '';

// Customize Error messages according to API Call.
switch ($api_call) {
case'settings/webhooks':
$log_level = LogLevel::WARNING;
break;

case (preg_match('/filters\?name=*/', $api_call) ? TRUE : FALSE):
case (preg_match('/settings\/clients\/*/', $api_call) ? TRUE : FALSE):
case (preg_match('/settings\/webhooks\/.*\/filters/', $api_call) ? TRUE : FALSE):
$log_level = LogLevel::NOTICE;
break;

default:
// The default log level is ERROR.
$log_level = LogLevel::ERROR;
break;
}

$reason = sprintf("Request ID: %s, Method: %s, Path: \"%s\", Status Code: %s, Reason: %s, Error Code: %s, Error Message: \"%s\". Error data: \"%s\"",
$response_body['request_id'] ?? '',
strtoupper($method),
$api_call,
$response->getStatusCode(),
$response->getReasonPhrase(),
$error_code,
$error_message,
print_r($response_body['error']['data'] ?? $response_body['error'] ?? '', TRUE)
);
$this->logger->log($log_level, $reason);

// Return the response.
return $response;
}

/**
* Returns error response.
*
* @param int $code
* Status code.
* @param string $reason
* Reason.
* @param string|null $request_id
* The request id from the ContentHub service if available.
*
* @return \Psr\Http\Message\ResponseInterface
* Response.
*/
protected function getErrorResponse(int $code, string $reason, ?string $request_id = NULL): ResponseInterface {
if ($code < 100 || $code >= 600) {
$code = 500;
}
$body = [
'request_id' => $request_id,
'error' => [
'code' => $code,
'message' => $reason,
],
];
return new Response($code, [], json_encode($body), '1.1', $reason);
}

/**
* Make a base url out of components and add a trailing slash to it.
*
* @param string[] $base_url_components
* Base URL components.
*
* @return string
* Processed string.
*/
protected static function makeBaseURL(...$base_url_components): string { // phpcs:ignore
return self::makePath(...$base_url_components) . '/';
}

/**
* Make path out of its individual components.
*
* @param string[] $path_components
* Path components.
*
* @return string
* Processed string.
*/
protected static function makePath(...$path_components): string { // phpcs:ignore
return self::gluePartsTogether($path_components, '/');
}

/**
* Glue all elements of an array together.
*
* @param array $parts
* Parts array.
* @param string $glue
* Glue symbol.
*
* @return string
* Processed string.
*/
protected static function gluePartsTogether(array $parts, string $glue): string {
return implode($glue, self::removeAllLeadingAndTrailingSlashes($parts));
}

/**
* Removes all leading and trailing slashes.
*
* Strip all leading and trailing slashes from all components of the given
* array.
*
* @param string[] $components
* Array of strings.
*
* @return string[]
* Processed array.
*/
protected static function removeAllLeadingAndTrailingSlashes(array $components): array {
return array_map(function ($component) {
return trim($component, '/');
}, $components);
}

/**
* Attaches RequestResponseHandler to handlers stack.
*
* @param array $config
* Client config.
*
* @codeCoverageIgnore
*/
protected function addRequestResponseHandler(array $config): void {
if (empty($config['handler']) || empty($this->logger)) {
return;
}

if (!$config['handler'] instanceof HandlerStack) {
return;
}

$config['handler']->push(new RequestResponseHandler($this->logger));
}

/**
* Appends search criteria header.
*
Expand Down
Loading

0 comments on commit f1fecc8

Please sign in to comment.