feat: shortened and filename-safe request ids
This commit is contained in:
@@ -1197,8 +1197,8 @@ Persists INFO+ level logs to `LogEntry` entity. Replaces message placeholders wi
|
||||
When debug mode is enabled, API requests/responses are dumped to `var/bpn/`:
|
||||
|
||||
```
|
||||
{request_id}_1_request.xml
|
||||
{request_id}_1_response.xml
|
||||
r-00m8z7k58a-8k1pvd9q_1_request.xml
|
||||
r-00m8z7k58a-8k1pvd9q_1_response.xml
|
||||
```
|
||||
|
||||
View via Admin Panel: Admin → Log → XML Dumps action.
|
||||
|
||||
@@ -22,6 +22,7 @@ use App\BusProNet\Model\Travel;
|
||||
use App\BusProNet\XmlParser\ApiResponseParser;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Model\RegistrationDto;
|
||||
use App\Service\RequestIdGenerator;
|
||||
use League\Flysystem\FilesystemException;
|
||||
use League\Flysystem\FilesystemOperator;
|
||||
use Psr\Log\LoggerInterface;
|
||||
@@ -58,6 +59,7 @@ class ApiClient
|
||||
private readonly LoggerInterface $logger,
|
||||
private readonly BookingDataProcessor $bookingDataProcessor,
|
||||
private readonly RequestStack $requestStack,
|
||||
private readonly RequestIdGenerator $requestIdGenerator,
|
||||
array $options,
|
||||
) {
|
||||
$this->config = $this->resolveOptions($options);
|
||||
@@ -762,7 +764,7 @@ class ApiClient
|
||||
private function getRequestId(): string
|
||||
{
|
||||
$baseId = $this->requestStack->getMainRequest()?->attributes->get('request_id')
|
||||
?? date(DATE_ATOM);
|
||||
?? $this->requestIdGenerator->generateBaseId();
|
||||
|
||||
return $baseId.'_'.++$this->requestCounter;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\EventListener;
|
||||
|
||||
use App\Service\RequestIdGenerator;
|
||||
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
|
||||
use Symfony\Component\HttpKernel\Event\RequestEvent;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
@@ -11,13 +12,18 @@ use Symfony\Component\HttpKernel\KernelEvents;
|
||||
#[AsEventListener(event: KernelEvents::REQUEST, priority: 255)]
|
||||
class RequestIdListener
|
||||
{
|
||||
public function __construct(
|
||||
private readonly RequestIdGenerator $requestIdGenerator,
|
||||
) {
|
||||
}
|
||||
|
||||
public function __invoke(RequestEvent $event): void
|
||||
{
|
||||
if (false === $event->isMainRequest()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$requestId = date(DATE_ATOM).uniqid();
|
||||
$requestId = $this->requestIdGenerator->generateBaseId();
|
||||
$event->getRequest()->attributes->set('request_id', $requestId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
class RequestIdGenerator
|
||||
{
|
||||
private const TIMESTAMP_LENGTH = 10;
|
||||
private const RANDOM_LENGTH = 8;
|
||||
|
||||
public function generateBaseId(): string
|
||||
{
|
||||
$timestampMs = (int) floor(microtime(true) * 1000);
|
||||
$timestampPart = str_pad(base_convert((string) $timestampMs, 10, 36), self::TIMESTAMP_LENGTH, '0', STR_PAD_LEFT);
|
||||
|
||||
$maxRandomValue = (36 ** self::RANDOM_LENGTH) - 1;
|
||||
$randomPart = str_pad(base_convert((string) random_int(0, $maxRandomValue), 10, 36), self::RANDOM_LENGTH, '0', STR_PAD_LEFT);
|
||||
|
||||
return sprintf('r-%s-%s', $timestampPart, $randomPart);
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ use App\BusProNet\ApiClient;
|
||||
use App\BusProNet\DataProcessor\BookingDataProcessor;
|
||||
use App\BusProNet\Exception\ImmediateConnectionCloseException;
|
||||
use App\BusProNet\XmlParser\ApiResponseParser;
|
||||
use App\Service\RequestIdGenerator;
|
||||
use League\Flysystem\FilesystemOperator;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Log\LoggerInterface;
|
||||
@@ -27,6 +28,7 @@ class ApiClientReceiveTest extends TestCase
|
||||
$this->createMock(LoggerInterface::class),
|
||||
$this->createMock(BookingDataProcessor::class),
|
||||
new RequestStack(),
|
||||
new RequestIdGenerator(),
|
||||
[
|
||||
'bpn_username' => 'test',
|
||||
'bpn_password' => 'test',
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\Service;
|
||||
|
||||
use App\Service\RequestIdGenerator;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class RequestIdGeneratorTest extends TestCase
|
||||
{
|
||||
public function testGeneratesFilenameSafeRequestId(): void
|
||||
{
|
||||
$generator = new RequestIdGenerator();
|
||||
|
||||
$requestId = $generator->generateBaseId();
|
||||
|
||||
self::assertMatchesRegularExpression('/^r-[0-9a-z]{10}-[0-9a-z]{8}$/', $requestId);
|
||||
}
|
||||
|
||||
public function testGeneratedIdsAreSortableByTime(): void
|
||||
{
|
||||
$generator = new RequestIdGenerator();
|
||||
|
||||
$first = $generator->generateBaseId();
|
||||
usleep(2_000);
|
||||
$second = $generator->generateBaseId();
|
||||
usleep(2_000);
|
||||
$third = $generator->generateBaseId();
|
||||
|
||||
self::assertTrue($first < $second);
|
||||
self::assertTrue($second < $third);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user