<?php
namespace App\Services\Main;
use App\Enum\ApiCodeEnum;
use App\Object\Api\CallResponse;
use App\Services\Util\ConfigService;
use App\Services\Util\JsonService;
use App\Services\Util\UtilService;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpClient\CurlHttpClient;
use Symfony\Component\HttpClient\HttpClient;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
use Throwable;
class ApiRequestService
{
/**
* @var JsonService
*/
protected JsonService $jsonService;
/**
* @var LoggerInterface
*/
protected LoggerInterface $logger;
/**
* @var ConfigService
*/
private ConfigService $configService;
/**
* @var UtilService
*/
private UtilService $utilService;
private SessionInterface $session;
/**
* @var CurlHttpClient
*/
private CurlHttpClient $client;
/**
* @var mixed
*/
private $clientIdentifier;
/**
* @var mixed
*/
private $apiUrl;
/**
* ApiRequestService constructor.
*
* @param ConfigService $configService
* @param JsonService $jsonService
* @param UtilService $utilService
* @param LoggerInterface $logger
*/
public function __construct(
ConfigService $configService,
JsonService $jsonService,
UtilService $utilService,
SessionInterface $session,
LoggerInterface $logger
) {
$this->configService = $configService;
$this->jsonService = $jsonService;
$this->utilService = $utilService;
$this->session = $session;
$this->logger = $logger;
$this->client = HttpClient::create();
$this->apiUrl = $this->configService->getApiUrl();
$this->clientIdentifier = $this->configService->getParameter('api_client_id');
}
/**
* @param string $method
* @param string $endpoint
* @param array $options
* @param null $defaultResult
* @param bool $debug
*
* @return CallResponse
*/
public function request(
string $method,
string $endpoint,
array $options = [],
$defaultResult = null,
bool $debug = false
): CallResponse {
// Populate ClientIdentifier in either json or body param
$bodyKey = isset($options['json']) ? 'json' : 'body';
$options[$bodyKey] ??= [];
$options[$bodyKey]['clientId'] = $this->clientIdentifier;
$options['timeout'] = 3600;
$impersonating = $this->session->get('impersonating');
if (!empty($impersonating)) {
$options[$bodyKey]['previousUserId'] = $impersonating['previousUser']->getId();
}
$requestResponse = new CallResponse();
try {
$url = $this->apiUrl . $endpoint;
$response = $this->client->request($method, $url, $options);
$requestResponse->setUrl($url);
$requestResponse->setStatusCode($response->getStatusCode());
$requestResponse->setContent(
$this->formatResponseContent($url, $response->toArray(), $defaultResult, $debug)
);
if ($debug) {
$requestResponse->debug();
} else {
$responseError = json_decode($response->getContent(false), true);
if (isset($responseError['error']) && $responseError['error']) {
if (is_array($responseError['message'])) {
$requestResponse->setErrorKey($responseError['message']);
} else {
$requestResponse->setErrorKey('api.error.' . $responseError['message']);
}
}
}
} catch (ClientExceptionInterface $e) {
$requestResponse->setErrorKey('api.error.client');
} catch (ServerExceptionInterface $e) {
$requestResponse->setErrorKey('api.error.server');
} catch (RedirectionExceptionInterface $e) {
$requestResponse->setErrorKey('api.error.redirection');
} catch (TransportExceptionInterface $e) {
$requestResponse->setErrorKey('api.error.transport');
} catch (DecodingExceptionInterface $e) {
$requestResponse->setErrorKey('api.error.decoding');
} catch (Throwable $e) {
$requestResponse->setErrorKey($e->getMessage());
}
return $requestResponse;
}
/**
* @param string $message
*
* @return bool
*/
public function messageIsErrorCode(string $message): bool
{
return in_array($message, ApiCodeEnum::LIST_ERROR_CODES)
|| in_array($message, ApiCodeEnum::LIST_WARNING_CODES)
|| in_array($message, ApiCodeEnum::LIST_INFO_CODES);
}
/**
* @param string $url
* @param array $responseContent
* @param $defaultContent
* @param bool $debug
*
* @throws \Symfony\Component\Serializer\Exception\ExceptionInterface
*
* @return array|object
*/
public function formatResponseContent(string $url, array $responseContent, $defaultContent, bool $debug)
{
$formattedResponseContent = [
'error' => false,
'message' => '',
'content' => $defaultContent,
];
if (!$debug && isset($responseContent['error']) && $responseContent['error']) {
$formattedResponseContent['error'] = true;
if ($this->isProductSaveRequest($url)) {
if (is_array($responseContent['message']) && $this->productResultContainsErrorCode($responseContent['message'])) {
$formattedResponseContent['message'] = $this->translateProductResultErrors($responseContent['message']);
} else {
$errorKey = $this->messageIsErrorCode($responseContent['message'])
? $responseContent['message']
: 'default';
$formattedResponseContent['message'] = $this->utilService->translate("api.error.{$errorKey}");
}
} else {
$errorKey = $this->messageIsErrorCode($responseContent['message'])
? $responseContent['message']
: 'default';
$formattedResponseContent['message'] = $this->utilService->translate("api.error.{$errorKey}");
}
} elseif (!array_key_exists('content', $responseContent)) {
$formattedResponseContent['content'] = $responseContent;
} else {
$formattedResponseContent = $responseContent;
}
return $this->jsonService->denormalize(
$formattedResponseContent,
'App\Object\Api\Content'
);
}
/**
* @param string $url
*
* @return bool
*/
public function isProductSaveRequest(string $url): bool
{
return str_contains($url, 'pim/product/save');
}
/**
* @param array $result
*
* @return bool
*/
public function productResultContainsErrorCode(array $result): bool
{
foreach ($result as $channelCode => $listLanguages) {
foreach ($listLanguages as $languageCode => $listAttributes) {
foreach ($listAttributes as $attributeCode => $listMessage) {
foreach ($listMessage as $message) {
preg_match_all('/[_[:upper:]]+/', $message, $listErrorCodes);
foreach ($listErrorCodes[0] as $errorCode) {
return $this->messageIsErrorCode($errorCode);
}
}
}
}
}
return false;
}
/**
* @param array $result
*
* @return array
*/
public function translateProductResultErrors(array $result): array
{
$listMessages = [];
foreach ($result as $channelCode => $listLanguages) {
foreach ($listLanguages as $languageCode => $listAttributes) {
foreach ($listAttributes as $attributeCode => $listMessage) {
foreach ($listMessage as $message) {
preg_match_all('/[_[:upper:]]+/', $message, $listErrorCodes);
foreach ($listErrorCodes[0] as $errorCode) {
if ($this->messageIsErrorCode($errorCode)) {
if (!isset($listMessages[$channelCode][$languageCode][$attributeCode])) {
$listMessages[$channelCode][$languageCode][$attributeCode] = [];
}
$listMessages[$channelCode][$languageCode][$attributeCode][] = str_replace("{$errorCode} : ", '', $message);
}
}
}
}
}
}
return $listMessages;
}
}