diff --git a/.docker/velox.toml b/.docker/velox.toml index 902d57f..832a17b 100644 --- a/.docker/velox.toml +++ b/.docker/velox.toml @@ -93,4 +93,9 @@ repository = "tcp" [github.plugins.smtp-server] ref = "2.0.0" owner = "buggregator" -repository = "smtp-server" \ No newline at end of file +repository = "smtp-server" + +[github.plugins.var-dumper-server] +ref = "1.0.0" +owner = "buggregator" +repository = "var-dumper-server" \ No newline at end of file diff --git a/.rr-prod.yaml b/.rr-prod.yaml index 250033d..e232742 100644 --- a/.rr-prod.yaml +++ b/.rr-prod.yaml @@ -13,32 +13,26 @@ server: command: "php app.php register:modules" logs: - # Logging mode can be "development", "production" or "raw". - # Do not forget to change this value for production environment. mode: ${RR_LOG_MODE:-production} - # Encoding format can be "console" or "json" (last is preferred for production usage). encoding: ${RR_LOG_ENCODING:-json} - # Logging level can be "panic", "error", "warn", "info", "debug". level: ${RR_LOG_LEVEL:-warn} channels: http: - # HTTP plugin logging level can be "panic", "error", "warn", "info", "debug". level: ${RR_LOG_HTTP_LEVEL:-warn} tcp: - # TCP plugin logging level can be "panic", "error", "warn", "info", "debug". level: ${RR_LOG_TCP_LEVEL:-warn} jobs: - # JOBS plugin logging level can be "panic", "error", "warn", "info", "debug". level: ${RR_LOG_JOBS_LEVEL:-warn} centrifuge: - # Centrifuge plugin logging level can be "panic", "error", "warn", "info", "debug". level: ${RR_LOG_CENTRIFUGE_LEVEL:-warn} server: - # Server logging level can be "panic", "error", "warn", "info", "debug". level: ${RR_LOG_SERVER_LEVEL:-warn} service: - # Service logging level can be "panic", "error", "warn", "info", "debug". level: ${RR_LOG_SERVICE_LEVEL:-warn} + smtp: + level: ${RR_LOG_SMTP_LEVEL:-warn} + var-dumper: + level: ${RR_LOG_VAR_DUMPER_LEVEL:-warn} http: address: 127.0.0.1:8082 @@ -57,20 +51,11 @@ http: tcp: servers: monolog: - # Address to listen. addr: ${RR_TCP_MONOLOG_ADDR:-:9913} delimiter: "\n" - var-dumper: - # Address to listen. - addr: ${RR_TCP_VAR_DUMPER_ADDR:-:9912} - delimiter: "\n" - # Chunks that RR uses to read the data. In bytes. - # If you expect big payloads on a TCP server, to reduce `read` syscalls, - # would be a good practice to use a fairly big enough buffer. - # Default: 1024 * 1024 * 50 (50MB) read_buf_size: ${RR_TCP_READ_BUF_SIZE:-50485760} pool: - num_workers: ${RR_TCP_NUM_WORKERS:-2} + num_workers: ${RR_TCP_NUM_WORKERS:-1} kv: local: @@ -79,9 +64,9 @@ kv: jobs: consume: - - smtp + - events pipelines: - smtp: + events: driver: memory config: priority: 10 @@ -93,7 +78,13 @@ smtp: addr: ${RR_SMTP_ADDR:-:1025} hostname: "buggregator.local" jobs: - pipeline: smtp + pipeline: events + +var-dumper: + addr: ${RR_VAR_DUMPER_ADDR:-:9912} + max_message_size: ${RR_TCP_READ_BUF_SIZE:-50485760} + jobs: + pipeline: events service: nginx: diff --git a/app/config/queue.php b/app/config/queue.php index 84dca51..1b4f41a 100644 --- a/app/config/queue.php +++ b/app/config/queue.php @@ -3,6 +3,7 @@ declare(strict_types=1); use Modules\Smtp\Interfaces\Jobs\EmailHandler; +use Modules\VarDumper\Interfaces\Jobs\DumpHandler; use Modules\Webhooks\Interfaces\Job\WebhookHandler; use Spiral\Queue\Driver\SyncDriver; use Spiral\RoadRunner\Jobs\Queue\MemoryCreateInfo; @@ -39,6 +40,7 @@ 'registry' => [ 'handlers' => [ 'smtp.email' => EmailHandler::class, + 'vardumper.dump' => DumpHandler::class, ], 'serializers' => [ WebhookHandler::class => 'symfony-json', diff --git a/app/config/tcp.php b/app/config/tcp.php index 9f01607..8ef903f 100644 --- a/app/config/tcp.php +++ b/app/config/tcp.php @@ -3,12 +3,10 @@ declare(strict_types=1); use App\Application\TCP\ExceptionHandlerInterceptor; -use Modules\VarDumper\Interfaces\TCP\Service as VarDumperService; use Modules\Monolog\Interfaces\TCP\Service as MonologService; return [ 'services' => [ - 'var-dumper' => VarDumperService::class, 'monolog' => MonologService::class, ], diff --git a/app/modules/VarDumper/Interfaces/TCP/Service.php b/app/modules/VarDumper/Interfaces/Jobs/DumpHandler.php similarity index 69% rename from app/modules/VarDumper/Interfaces/TCP/Service.php rename to app/modules/VarDumper/Interfaces/Jobs/DumpHandler.php index 5dbd224..595e25e 100644 --- a/app/modules/VarDumper/Interfaces/TCP/Service.php +++ b/app/modules/VarDumper/Interfaces/Jobs/DumpHandler.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Modules\VarDumper\Interfaces\TCP; +namespace Modules\VarDumper\Interfaces\Jobs; use App\Application\Commands\HandleReceivedEvent; use Modules\VarDumper\Application\Dump\BodyInterface; @@ -12,41 +12,32 @@ use Modules\VarDumper\Application\Dump\MessageParser; use Modules\VarDumper\Application\Dump\ParsedPayload; use Modules\VarDumper\Application\Dump\PrimitiveBody; +use Spiral\Core\InvokerInterface; use Spiral\Cqrs\CommandBusInterface; -use Spiral\RoadRunner\Tcp\Request; -use Spiral\RoadRunner\Tcp\TcpEvent; -use Spiral\RoadRunnerBridge\Tcp\Response\ContinueRead; -use Spiral\RoadRunnerBridge\Tcp\Response\ResponseInterface; -use Spiral\RoadRunnerBridge\Tcp\Service\ServiceInterface; +use Spiral\Queue\JobHandler; use Symfony\Component\VarDumper\Cloner\Data; -final readonly class Service implements ServiceInterface +final class DumpHandler extends JobHandler { public function __construct( - private CommandBusInterface $commandBus, - private DumpIdGeneratorInterface $dumpId, - ) {} + private readonly CommandBusInterface $bus, + private readonly DumpIdGeneratorInterface $dumpId, + InvokerInterface $invoker, + ) { + parent::__construct($invoker); + } - public function handle(Request $request): ResponseInterface + public function invoke(mixed $payload): void { - if ($request->event === TcpEvent::Connected) { - return new ContinueRead(); - } - - $messages = \array_filter(\explode("\n", $request->body)); - - foreach ($messages as $message) { - $payload = (new MessageParser())->parse($message); - - $this->fireEvent($payload); - } - - return new ContinueRead(); + $this->fireEvent( + (new MessageParser())->parse($payload), + ); } + private function fireEvent(ParsedPayload $payload): void { - $this->commandBus->dispatch( + $this->bus->dispatch( new HandleReceivedEvent( type: 'var-dump', payload: [ @@ -96,4 +87,5 @@ private function prepareContent(ParsedPayload $payload): array return $payloadContent; } + } diff --git a/docker-compose.yaml b/docker-compose.yaml index 5f32c78..241ec3e 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -31,7 +31,7 @@ services: args: GH_TOKEN: "${GH_TOKEN}" environment: -# RR_LOG_LEVEL: debug + RR_LOG_LEVEL: debug # RR_LOG_TCP_LEVEL: debug # RR_LOG_JOBS_LEVEL: debug # RR_LOG_SERVER_LEVEL: debug diff --git a/tests/Feature/Interfaces/TCP/TCPTestCase.php b/tests/Feature/Interfaces/TCP/TCPTestCase.php index 5d4375f..df0c3e0 100644 --- a/tests/Feature/Interfaces/TCP/TCPTestCase.php +++ b/tests/Feature/Interfaces/TCP/TCPTestCase.php @@ -5,15 +5,9 @@ namespace Tests\Feature\Interfaces\TCP; use Modules\Monolog\Interfaces\TCP\Service as MonologService; -use Modules\VarDumper\Interfaces\TCP\Service as VarDumperService; -use Modules\Smtp\Interfaces\TCP\Service as SmtpService; -use Ramsey\Uuid\Uuid; -use Ramsey\Uuid\UuidInterface; use Spiral\RoadRunner\Tcp\Request; use Spiral\RoadRunner\Tcp\TcpEvent; use Spiral\RoadRunnerBridge\Tcp\Response\ResponseInterface; -use Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport; -use Tests\App\Smtp\FakeStream; use Tests\DatabaseTestCase; abstract class TCPTestCase extends DatabaseTestCase @@ -25,20 +19,6 @@ public function handleMonologRequest(string $message): ResponseInterface ->handle($this->buildRequest(message: $message)); } - public function handleVarDumperRequest(string $message): ResponseInterface - { - return $this - ->get(VarDumperService::class) - ->handle($this->buildRequest(message: $message)); - } - - public function handleSmtpRequest(string $message, TcpEvent $event = TcpEvent::Data): ResponseInterface - { - return $this - ->get(SmtpService::class) - ->handle($this->buildRequest(message: $message, event: $event)); - } - private function buildRequest(string $message, TcpEvent $event = TcpEvent::Data): Request { return new Request( @@ -49,19 +29,4 @@ private function buildRequest(string $message, TcpEvent $event = TcpEvent::Data) server: 'localhost', ); } - - protected function buildSmtpClient(string $username = 'homestead', ?UuidInterface $uuid = null): EsmtpTransport - { - $client = new EsmtpTransport( - stream: new FakeStream( - service: $this->get(SmtpService::class), - uuid: (string) $uuid ?? Uuid::uuid7(), - ), - ); - - $client->setUsername($username); - $client->setPassword('password'); - - return $client; - } } diff --git a/tests/Feature/Interfaces/TCP/VarDumper/SymfonyV6Test.php b/tests/Feature/Interfaces/TCP/VarDumper/SymfonyV6Test.php deleted file mode 100644 index 1614802..0000000 --- a/tests/Feature/Interfaces/TCP/VarDumper/SymfonyV6Test.php +++ /dev/null @@ -1,35 +0,0 @@ -handleVarDumperRequest($payload); - - $this->broadcastig->assertPushed(new EventsChannel('default'), function (array $data) { - $this->assertSame('event.received', $data['event']); - $this->assertSame('var-dump', $data['data']['type']); - - $this->assertSame([ - 'type' => 'string', - 'value' => 'foo', - 'label' => null, - ], $data['data']['payload']['payload']); - - $this->assertNotEmpty($data['data']['uuid']); - $this->assertNotEmpty($data['data']['timestamp']); - - - return true; - }); - } -} diff --git a/tests/Feature/Interfaces/TCP/VarDumper/SymfonyV7Test.php b/tests/Feature/Interfaces/TCP/VarDumper/SymfonyV7Test.php deleted file mode 100644 index 774222f..0000000 --- a/tests/Feature/Interfaces/TCP/VarDumper/SymfonyV7Test.php +++ /dev/null @@ -1,148 +0,0 @@ - ['foo', 'string', 'foo']; - yield 'true' => [true, 'boolean', '1']; - yield 'false' => [false, 'boolean', '0']; - yield 'int' => [1, 'integer', '1']; - yield 'float' => [1.1, 'double', '1.1']; - yield 'array' => [ - ['foo' => 'bar'], - 'array', - <<<'HTML' -
Some label array:1 [
-  "foo" => "bar"
-]
-
- -HTML - , - ]; - yield 'object' => [ - (object) ['type' => 'string', 'value' => 'foo'], - 'stdClass', - <<Some label {#%s - +"type": "string" - +"value": "foo" -} - - -HTML - , - ]; - } - - #[DataProvider('variablesDataProvider')] - public function testSendDump(mixed $value, string $type, mixed $expected): void - { - $generator = $this->mockContainer(DumpIdGeneratorInterface::class); - $generator->shouldReceive('generate')->andReturn('sf-dump-730421088'); - - $message = $this->buildPayload(var: $value); - $this->handleVarDumperRequest($message); - - if (\is_object($value)) { - $expected = \sprintf($expected, \spl_object_id($value)); - } - - $this->broadcastig->assertPushed(new EventsChannel('default'), function (array $data) use ($value, $type, $expected) { - $this->assertSame('event.received', $data['event']); - $this->assertSame('var-dump', $data['data']['type']); - - $this->assertSame([ - 'type' => $type, - 'value' => $expected, - 'label' => 'Some label', - ], $data['data']['payload']['payload']); - - $this->assertNotEmpty($data['data']['uuid']); - $this->assertNotEmpty($data['data']['timestamp']); - - return true; - }); - } - - public function testSendDumpWithCodeHighlighting(): void - { - $message = $this->buildPayload(var: 'foo', context: ['language' => 'php']); - $this->handleVarDumperRequest($message); - - $this->broadcastig->assertPushed(new EventsChannel('default'), function (array $data) { - $this->assertSame('event.received', $data['event']); - $this->assertSame('var-dump', $data['data']['type']); - - $this->assertSame([ - 'type' => 'code', - 'value' => 'foo', - 'label' => 'Some label', - 'language' => 'php', - ], $data['data']['payload']['payload']); - - return true; - }); - } - - public function testSendDumpWithProject(): void - { - $this->createProject('foo'); - $message = $this->buildPayload(project: 'foo'); - $this->handleVarDumperRequest($message); - - $this->broadcastig->assertPushed(new EventsChannel('foo'), function (array $data) { - $this->assertSame('foo', $data['data']['project']); - return true; - }); - } - - public function testSendDumpWithNonExistsProject(): void - { - $message = $this->buildPayload(project: 'foo'); - $this->handleVarDumperRequest($message); - - $this->broadcastig->assertNotPushed(new EventsChannel('foo')); - $this->broadcastig->assertPushed(new EventsChannel('default'), function (array $data) { - $this->assertSame('default', $data['data']['project']); - return true; - }); - } - - public function testSendInvalidDump(): void - { - $this->expectException(InvalidPayloadException::class); - $this->expectExceptionMessage('Unable to decode the message.'); - - $this->handleVarDumperRequest('invalid'); - } - - private function buildPayload(mixed $var = 'string', ?string $project = null, array $context = []): string - { - $cloner = new VarCloner(); - $cloner->addCasters(ReflectionCaster::UNSET_CLOSURE_FILE_INFO); - - $data = $cloner->cloneVar($var); - - if ($project !== null) { - $context['project'] = $project; - } - - $context['label'] = 'Some label'; - - return \base64_encode(\serialize([$data->withContext($context), []])) . "\n"; - } -}