Skip to content

Commit

Permalink
Allow to restore original pushed message class on consume
Browse files Browse the repository at this point in the history
  • Loading branch information
viktorprogger committed Sep 15, 2024
1 parent d231e82 commit 00ad4a6
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 18 deletions.
12 changes: 12 additions & 0 deletions src/Message/EnvelopeTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,18 @@ trait EnvelopeTrait
{
private MessageInterface $message;

/**
* A mirror of {@see MessageInterface::fromData()}
*/
abstract public static function fromMessage(MessageInterface $message): self;

public static function fromData(string $handlerName, mixed $data, array $metadata = []): MessageInterface
{
$message = new Message($handlerName, $data, $metadata);

return self::fromMessage($message);
}

public function getMessage(): MessageInterface
{
return $this->message;
Expand Down
35 changes: 24 additions & 11 deletions src/Message/JsonMessageSerializer.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public function serialize(MessageInterface $message): string
'name' => $message->getHandlerName(),
'data' => $message->getData(),
'meta' => $message->getMetadata(),
'class' => $message instanceof EnvelopeInterface ? get_class($message->getMessage()) : get_class($message),
];

return json_encode($payload, JSON_THROW_ON_ERROR);
Expand All @@ -34,25 +35,37 @@ public function unserialize(string $value): MessageInterface
throw new InvalidArgumentException('Payload must be array. Got ' . get_debug_type($payload) . '.');
}

$name = $payload['name'] ?? null;
if (!isset($name) || !is_string($name)) {
throw new InvalidArgumentException('Handler name must be string. Got ' . get_debug_type($name) . '.');
}

$meta = $payload['meta'] ?? [];
if (!is_array($meta)) {
throw new InvalidArgumentException('Metadata must be array. Got ' . get_debug_type($meta) . '.');
}

// TODO: will be removed later
$message = new Message($payload['name'] ?? '$name', $payload['data'] ?? null, $meta);

$envelopes = [];
if (isset($meta[EnvelopeInterface::ENVELOPE_STACK_KEY]) && is_array($meta[EnvelopeInterface::ENVELOPE_STACK_KEY])) {
$message = $message->withMetadata(
array_merge($message->getMetadata(), [EnvelopeInterface::ENVELOPE_STACK_KEY => []]),
);
foreach ($meta[EnvelopeInterface::ENVELOPE_STACK_KEY] as $envelope) {
if (is_string($envelope) && class_exists($envelope) && is_subclass_of($envelope, EnvelopeInterface::class)) {
$message = $envelope::fromMessage($message);
}
}
$envelopes = $meta[EnvelopeInterface::ENVELOPE_STACK_KEY];
}
$meta[EnvelopeInterface::ENVELOPE_STACK_KEY] = [];

$class = $payload['class'] ?? Message::class;
if (!is_subclass_of($class, MessageInterface::class)) {
$class = Message::class;
}

/**
* @var class-string<MessageInterface> $class
*/
$message = $class::fromData($name, $payload['data'] ?? null, $meta);

foreach ($envelopes as $envelope) {
if (is_string($envelope) && class_exists($envelope) && is_subclass_of($envelope, EnvelopeInterface::class)) {
$message = $envelope::fromMessage($message);
}
}

return $message;
}
Expand Down
5 changes: 5 additions & 0 deletions src/Message/Message.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ public function __construct(
) {
}

public static function fromData(string $handlerName, mixed $data, array $metadata = []): MessageInterface
{
return new self($handlerName, $data, $metadata);
}

public function getHandlerName(): string
{
return $this->handlerName;
Expand Down
2 changes: 2 additions & 0 deletions src/Message/MessageInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

interface MessageInterface
{
public static function fromData(string $handlerName, mixed $data, array $metadata = []): self;

/**
* Returns handler name.
*
Expand Down
36 changes: 29 additions & 7 deletions tests/Unit/Message/JsonMessageSerializerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use Yiisoft\Queue\Message\JsonMessageSerializer;
use Yiisoft\Queue\Message\Message;
use Yiisoft\Queue\Message\MessageInterface;
use Yiisoft\Queue\Tests\Unit\Support\TestMessage;

/**
* Testing message serialization options
Expand Down Expand Up @@ -42,7 +43,7 @@ public static function dataUnsupportedPayloadFormat(): iterable
*/
public function testMetadataFormat(mixed $meta): void
{
$payload = ['data' => 'test', 'meta' => $meta];
$payload = ['name' => 'handler', 'data' => 'test', 'meta' => $meta];
$serializer = $this->createSerializer();

$this->expectExceptionMessage(sprintf('Metadata must be array. Got %s.', get_debug_type($meta)));
Expand All @@ -59,31 +60,32 @@ public static function dataUnsupportedMetadataFormat(): iterable

public function testUnserializeFromData(): void
{
$payload = ['data' => 'test'];
$payload = ['name' => 'handler', 'data' => 'test'];
$serializer = $this->createSerializer();

$message = $serializer->unserialize(json_encode($payload));

$this->assertInstanceOf(MessageInterface::class, $message);
$this->assertEquals($payload['data'], $message->getData());
$this->assertEquals([], $message->getMetadata());
$this->assertEquals([EnvelopeInterface::ENVELOPE_STACK_KEY => []], $message->getMetadata());
}

public function testUnserializeWithMetadata(): void
{
$payload = ['data' => 'test', 'meta' => ['int' => 1, 'str' => 'string', 'bool' => true]];
$payload = ['name' => 'handler', 'data' => 'test', 'meta' => ['int' => 1, 'str' => 'string', 'bool' => true]];
$serializer = $this->createSerializer();

$message = $serializer->unserialize(json_encode($payload));

$this->assertInstanceOf(MessageInterface::class, $message);
$this->assertEquals($payload['data'], $message->getData());
$this->assertEquals(['int' => 1, 'str' => 'string', 'bool' => true], $message->getMetadata());
$this->assertEquals(['int' => 1, 'str' => 'string', 'bool' => true, EnvelopeInterface::ENVELOPE_STACK_KEY => []], $message->getMetadata());
}

public function testUnserializeEnvelopeStack(): void
{
$payload = [
'name' => 'handler',
'data' => 'test',
'meta' => [
EnvelopeInterface::ENVELOPE_STACK_KEY => [
Expand Down Expand Up @@ -113,7 +115,7 @@ public function testSerialize(): void
$json = $serializer->serialize($message);

$this->assertEquals(
'{"name":"handler","data":"test","meta":[]}',
'{"name":"handler","data":"test","meta":[],"class":"Yiisoft\\\\Queue\\\\Message\\\\Message"}',
$json,
);
}
Expand All @@ -129,9 +131,10 @@ public function testSerializeEnvelopeStack(): void

$this->assertEquals(
sprintf(
'{"name":"handler","data":"test","meta":{"envelopes":["%s"],"%s":"test-id"}}',
'{"name":"handler","data":"test","meta":{"envelopes":["%s"],"%s":"test-id"},"class":"%s"}',
str_replace('\\', '\\\\', IdEnvelope::class),
IdEnvelope::MESSAGE_ID_KEY,
str_replace('\\', '\\\\', Message::class),
),
$json,
);
Expand All @@ -153,6 +156,25 @@ public function testSerializeEnvelopeStack(): void
], $message->getMessage()->getMetadata());
}

public function testRestoreOriginalMessageClass(): void
{
$message = new TestMessage();
$serializer = $this->createSerializer();
$serializer->unserialize($serializer->serialize($message));

$this->assertInstanceOf(TestMessage::class, $message);
}

public function testRestoreOriginalMessageClassWithEnvelope(): void
{
$message = new IdEnvelope(new TestMessage());
$serializer = $this->createSerializer();
$serializer->unserialize($serializer->serialize($message));

$this->assertInstanceOf(IdEnvelope::class, $message);
$this->assertInstanceOf(TestMessage::class, $message->getMessage());
}

private function createSerializer(): JsonMessageSerializer
{
return new JsonMessageSerializer();
Expand Down
30 changes: 30 additions & 0 deletions tests/Unit/Support/TestMessage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Queue\Tests\Unit\Support;

use Yiisoft\Queue\Message\MessageInterface;

final class TestMessage implements MessageInterface
{
public static function fromData(string $handlerName, mixed $data, array $metadata = []): MessageInterface
{
return new self();
}

public function getHandlerName(): string
{
return 'test';
}

public function getData(): mixed
{
return null;
}

public function getMetadata(): array
{
return [];
}
}

0 comments on commit 00ad4a6

Please sign in to comment.