From 50cf106d811a2746c37b988b0727253707141a43 Mon Sep 17 00:00:00 2001 From: mesilov Date: Sat, 25 Nov 2023 01:36:35 +0600 Subject: [PATCH 1/3] add new line in changelog Signed-off-by: mesilov --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 81ff9ad6..2c93216c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ * add [crm item support](https://github.com/mesilov/bitrix24-php-sdk/issues/330) * add enum `DealStageSemanticId` * add Duplicate search support for `Bitrix24\SDK\Services\CRM\Duplicates\Service\Duplicate` +* add `x-request-id` [header support](https://github.com/mesilov/bitrix24-php-sdk/issues/354) ### Changed From 352e46d4535ab8305a9616faf446d3c3a1e104f3 Mon Sep 17 00:00:00 2001 From: mesilov Date: Sat, 25 Nov 2023 11:32:56 +0600 Subject: [PATCH 2/3] add request id generator proto Signed-off-by: mesilov --- src/Core/ApiClient.php | 46 +++++++++++++++---- src/Core/CoreBuilder.php | 22 ++++++--- .../RequestId/DefaultRequestIdGenerator.php | 22 +++++++++ .../RequestId/RequestIdGeneratorInterface.php | 12 +++++ tests/Unit/Stubs/NullCore.php | 12 ++--- 5 files changed, 93 insertions(+), 21 deletions(-) create mode 100644 src/Infrastructure/HttpClient/RequestId/DefaultRequestIdGenerator.php create mode 100644 src/Infrastructure/HttpClient/RequestId/RequestIdGeneratorInterface.php diff --git a/src/Core/ApiClient.php b/src/Core/ApiClient.php index ac7387e0..9f2f6125 100644 --- a/src/Core/ApiClient.php +++ b/src/Core/ApiClient.php @@ -5,9 +5,11 @@ namespace Bitrix24\SDK\Core; use Bitrix24\SDK\Core\Contracts\ApiClientInterface; +use Bitrix24\SDK\Core\Credentials\Credentials; use Bitrix24\SDK\Core\Exceptions\InvalidArgumentException; use Bitrix24\SDK\Core\Exceptions\TransportException; use Bitrix24\SDK\Core\Response\DTO\RenewedAccessToken; +use Bitrix24\SDK\Infrastructure\HttpClient\RequestId\RequestIdGeneratorInterface; use Fig\Http\Message\StatusCodeInterface; use Psr\Log\LoggerInterface; use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; @@ -18,7 +20,8 @@ class ApiClient implements ApiClientInterface { protected HttpClientInterface $client; protected LoggerInterface $logger; - protected Credentials\Credentials $credentials; + protected Credentials $credentials; + protected RequestIdGeneratorInterface $requestIdGenerator; /** * @const string */ @@ -32,14 +35,20 @@ class ApiClient implements ApiClientInterface /** * ApiClient constructor. * - * @param Credentials\Credentials $credentials + * @param Credentials $credentials * @param HttpClientInterface $client + * @param RequestIdGeneratorInterface $requestIdGenerator * @param LoggerInterface $logger */ - public function __construct(Credentials\Credentials $credentials, HttpClientInterface $client, LoggerInterface $logger) + public function __construct( + Credentials $credentials, + HttpClientInterface $client, + RequestIdGeneratorInterface $requestIdGenerator, + LoggerInterface $logger) { $this->credentials = $credentials; $this->client = $client; + $this->requestIdGenerator = $requestIdGenerator; $this->logger = $logger; $this->logger->debug( 'ApiClient.init', @@ -64,9 +73,9 @@ protected function getDefaultHeaders(): array } /** - * @return Credentials\Credentials + * @return Credentials */ - public function getCredentials(): Credentials\Credentials + public function getCredentials(): Credentials { return $this->credentials; } @@ -80,7 +89,10 @@ public function getCredentials(): Credentials\Credentials */ public function getNewAccessToken(): RenewedAccessToken { - $this->logger->debug('getNewAccessToken.start'); + $requestId = $this->requestIdGenerator->getRequestId(); + $this->logger->debug('getNewAccessToken.start', [ + 'requestId' => $requestId + ]); if ($this->getCredentials()->getApplicationProfile() === null) { throw new InvalidArgumentException('application profile not set'); } @@ -103,14 +115,21 @@ public function getNewAccessToken(): RenewedAccessToken ); $requestOptions = [ - 'headers' => $this->getDefaultHeaders(), + 'headers' => array_merge( + $this->getDefaultHeaders(), + [ + $this->requestIdGenerator->getHeaderFieldName() => $requestId + ] + ), ]; $response = $this->client->request($method, $url, $requestOptions); $responseData = $response->toArray(false); if ($response->getStatusCode() === StatusCodeInterface::STATUS_OK) { $newAccessToken = RenewedAccessToken::initFromArray($responseData); - $this->logger->debug('getNewAccessToken.finish'); + $this->logger->debug('getNewAccessToken.finish', [ + 'requestId' => $requestId + ]); return $newAccessToken; } if ($response->getStatusCode() === StatusCodeInterface::STATUS_BAD_REQUEST) { @@ -129,12 +148,14 @@ public function getNewAccessToken(): RenewedAccessToken */ public function getResponse(string $apiMethod, array $parameters = []): ResponseInterface { + $requestId = $this->requestIdGenerator->getRequestId(); $this->logger->info( 'getResponse.start', [ 'apiMethod' => $apiMethod, 'domainUrl' => $this->credentials->getDomainUrl(), 'parameters' => $parameters, + 'requestId' => $requestId ] ); @@ -150,9 +171,15 @@ public function getResponse(string $apiMethod, array $parameters = []): Response $parameters['auth'] = $this->getCredentials()->getAccessToken()->getAccessToken(); } + $requestOptions = [ 'json' => $parameters, - 'headers' => $this->getDefaultHeaders(), + 'headers' => array_merge( + $this->getDefaultHeaders(), + [ + $this->requestIdGenerator->getHeaderFieldName() => $requestId + ] + ), // disable redirects, try to catch portal change domain name event 'max_redirects' => 0, ]; @@ -163,6 +190,7 @@ public function getResponse(string $apiMethod, array $parameters = []): Response [ 'apiMethod' => $apiMethod, 'responseInfo' => $response->getInfo(), + 'requestId' => $requestId ] ); diff --git a/src/Core/CoreBuilder.php b/src/Core/CoreBuilder.php index 8e5b99d8..8f0d7960 100644 --- a/src/Core/CoreBuilder.php +++ b/src/Core/CoreBuilder.php @@ -9,6 +9,8 @@ use Bitrix24\SDK\Core\Credentials\Credentials; use Bitrix24\SDK\Core\Credentials\WebhookUrl; use Bitrix24\SDK\Core\Exceptions\InvalidArgumentException; +use Bitrix24\SDK\Infrastructure\HttpClient\RequestId\DefaultRequestIdGenerator; +use Bitrix24\SDK\Infrastructure\HttpClient\RequestId\RequestIdGeneratorInterface; use Psr\Log\LoggerInterface; use Psr\Log\NullLogger; use Symfony\Component\EventDispatcher\EventDispatcher; @@ -23,12 +25,13 @@ */ class CoreBuilder { - protected ?ApiClientInterface $apiClient; - protected HttpClientInterface $httpClient; - protected EventDispatcherInterface $eventDispatcher; - protected LoggerInterface $logger; - protected ?Credentials $credentials; - protected ApiLevelErrorHandler $apiLevelErrorHandler; + private ?ApiClientInterface $apiClient; + private HttpClientInterface $httpClient; + private EventDispatcherInterface $eventDispatcher; + private LoggerInterface $logger; + private ?Credentials $credentials; + private ApiLevelErrorHandler $apiLevelErrorHandler; + private RequestIdGeneratorInterface $requestIdGenerator; /** * CoreBuilder constructor. @@ -46,6 +49,12 @@ public function __construct() $this->credentials = null; $this->apiClient = null; $this->apiLevelErrorHandler = new ApiLevelErrorHandler($this->logger); + $this->requestIdGenerator = new DefaultRequestIdGenerator(); + } + + public function withRequestIdGenerator(RequestIdGeneratorInterface $requestIdGenerator): void + { + $this->requestIdGenerator = $requestIdGenerator; } /** @@ -101,6 +110,7 @@ public function build(): CoreInterface $this->apiClient = new ApiClient( $this->credentials, $this->httpClient, + $this->requestIdGenerator, $this->logger ); } diff --git a/src/Infrastructure/HttpClient/RequestId/DefaultRequestIdGenerator.php b/src/Infrastructure/HttpClient/RequestId/DefaultRequestIdGenerator.php new file mode 100644 index 00000000..05fba394 --- /dev/null +++ b/src/Infrastructure/HttpClient/RequestId/DefaultRequestIdGenerator.php @@ -0,0 +1,22 @@ +toRfc4122(); + } + + public function getHeaderFieldName(): string + { + return 'X-Request-ID'; + } +} \ No newline at end of file diff --git a/src/Infrastructure/HttpClient/RequestId/RequestIdGeneratorInterface.php b/src/Infrastructure/HttpClient/RequestId/RequestIdGeneratorInterface.php new file mode 100644 index 00000000..60dd9f0f --- /dev/null +++ b/src/Infrastructure/HttpClient/RequestId/RequestIdGeneratorInterface.php @@ -0,0 +1,12 @@ + Date: Sun, 26 Nov 2023 02:23:22 +0600 Subject: [PATCH 3/3] first version Signed-off-by: mesilov --- .../RequestId/DefaultRequestIdGenerator.php | 36 +++++++++++++-- .../DefaultRequestIdGeneratorTest.php | 44 +++++++++++++++++++ 2 files changed, 76 insertions(+), 4 deletions(-) create mode 100644 tests/Unit/Infrastructure/HttpClient/RequestId/DefaultRequestIdGeneratorTest.php diff --git a/src/Infrastructure/HttpClient/RequestId/DefaultRequestIdGenerator.php b/src/Infrastructure/HttpClient/RequestId/DefaultRequestIdGenerator.php index 05fba394..d0332e8b 100644 --- a/src/Infrastructure/HttpClient/RequestId/DefaultRequestIdGenerator.php +++ b/src/Infrastructure/HttpClient/RequestId/DefaultRequestIdGenerator.php @@ -8,15 +8,43 @@ class DefaultRequestIdGenerator implements RequestIdGeneratorInterface { - public function getRequestId(): string + private const DEFAULT_REQUEST_ID_FIELD_NAME = 'X-Request-ID'; + private const KEY_NAME_VARIANTS = [ + 'REQUEST_ID', + 'HTTP_X_REQUEST_ID', + 'UNIQUE_ID' + ]; + + private function generate(): string { - // get from server fields - // if empty - generate return Uuid::v7()->toRfc4122(); } + private function findExists(): ?string + { + $candidate = null; + foreach(self::KEY_NAME_VARIANTS as $key) + { + if(!empty($_SERVER[$key])) + { + $candidate = $_SERVER[$key]; + break; + } + } + return $candidate; + } + + public function getRequestId(): string + { + $reqId = $this->findExists(); + if ($reqId === null) { + $reqId = $this->generate(); + } + return $reqId; + } + public function getHeaderFieldName(): string { - return 'X-Request-ID'; + return self::DEFAULT_REQUEST_ID_FIELD_NAME; } } \ No newline at end of file diff --git a/tests/Unit/Infrastructure/HttpClient/RequestId/DefaultRequestIdGeneratorTest.php b/tests/Unit/Infrastructure/HttpClient/RequestId/DefaultRequestIdGeneratorTest.php new file mode 100644 index 00000000..fab333b8 --- /dev/null +++ b/tests/Unit/Infrastructure/HttpClient/RequestId/DefaultRequestIdGeneratorTest.php @@ -0,0 +1,44 @@ +assertEquals($requestId, $gen->getRequestId()); + unset($_SERVER[$requestIdKey]); + } + + public function requestIdKeyDataProvider(): Generator + { + yield 'REQUEST_ID' => [ + 'REQUEST_ID', + Uuid::v7()->toRfc4122() + ]; + yield 'HTTP_X_REQUEST_ID' => [ + 'HTTP_X_REQUEST_ID', + Uuid::v7()->toRfc4122() + ]; + yield 'UNIQUE_ID' => [ + 'UNIQUE_ID', + Uuid::v7()->toRfc4122() + ]; + } +}