diff --git a/src/Message/EnvelopeInterface.php b/src/Message/EnvelopeInterface.php index 7c58daa1..5c8266c3 100644 --- a/src/Message/EnvelopeInterface.php +++ b/src/Message/EnvelopeInterface.php @@ -17,4 +17,17 @@ public static function fromMessage(MessageInterface $message): self; public function getMessage(): MessageInterface; public function withMessage(MessageInterface $message): self; + + /** + * Finds an envelope in the current envelope stack or creates a new one from the message. + * + * @template T of EnvelopeInterface + * + * @psalm-param class-string $className + * @throws NotEnvelopInterfaceException Implementation MUST throw this exception if the given class does not + * implement {@see EnvelopeInterface}. + * + * @psalm-return T + */ + public function getEnvelopeFromStack(string $className): self; } diff --git a/src/Message/EnvelopeTrait.php b/src/Message/EnvelopeTrait.php index cc52cf15..51f4440f 100644 --- a/src/Message/EnvelopeTrait.php +++ b/src/Message/EnvelopeTrait.php @@ -45,6 +45,42 @@ public function getMetadata(): array ); } + /** + * Finds an envelope in the current envelope stack or creates a new one from the message. + * + * @template T of EnvelopeInterface + * + * @psalm-param class-string $className + * @throws NotEnvelopInterfaceException is thrown if the given class does not implement {@see EnvelopeInterface}. + * + * @psalm-return T + */ + public function getEnvelopeFromStack(string $className): EnvelopeInterface + { + if (!is_a($className, EnvelopeInterface::class, true)) { + throw new NotEnvelopInterfaceException($className); + } + + if (static::class === $className) { + return $this; + } + + if ($this->message instanceof EnvelopeInterface) { + return $this->message->getEnvelopeFromStack($className); + } + + return $className::fromMessage($this->message); + } + + public static function getEnvelopeFromMessage(MessageInterface $message): EnvelopeInterface + { + if ($message instanceof EnvelopeInterface) { + return $message->getEnvelopeFromStack(static::class); + } + + return static::fromMessage($message); + } + public function getEnvelopeMetadata(): array { return []; diff --git a/src/Message/NotEnvelopInterfaceException.php b/src/Message/NotEnvelopInterfaceException.php new file mode 100644 index 00000000..f0461df4 --- /dev/null +++ b/src/Message/NotEnvelopInterfaceException.php @@ -0,0 +1,22 @@ + true]); + $result = $envelope->getEnvelopeFromStack(IdEnvelope::class); + + $this->assertInstanceOf(IdEnvelope::class, $result); + $this->assertEquals('id value', $result->getId()); + } + + public function testGetEnvelopeFromStackNotInStack(): void + { + $message = new Message('handler', 'data', [IdEnvelope::MESSAGE_ID_KEY => 'id value']); + $envelope = new FailureEnvelope($message, ['fail' => true]); + $result = $envelope->getEnvelopeFromStack(IdEnvelope::class); + + $this->assertInstanceOf(IdEnvelope::class, $result); + $this->assertEquals('id value', $result->getId()); + } + + public function testGetEnvelopeFromStackNotEnvelope(): void + { + $this->expectException(NotEnvelopInterfaceException::class); + $interface = EnvelopeInterface::class; + $this->expectExceptionMessage("The given class \"foo\" does not implement \"$interface\"."); + + $message = new Message('handler', 'data', [IdEnvelope::MESSAGE_ID_KEY => 'id value']); + $envelope = new FailureEnvelope($message, ['fail' => true]); + $envelope->getEnvelopeFromStack('foo'); + } + + public function testGetEnvelopeFromMessage(): void + { + $message = new Message('handler', 'data'); + $envelope = new FailureEnvelope(new IdEnvelope($message, 'id value'), ['fail' => true]); + $result = IdEnvelope::getEnvelopeFromMessage($envelope); + + $this->assertInstanceOf(IdEnvelope::class, $result); + $this->assertEquals('id value', $result->getId()); + } + + public function testGetEnvelopeFromMessageNotInStack(): void + { + $message = new Message('handler', 'data', [IdEnvelope::MESSAGE_ID_KEY => 'id value']); + $result = IdEnvelope::getEnvelopeFromMessage($message); + + $this->assertInstanceOf(IdEnvelope::class, $result); + $this->assertEquals('id value', $result->getId()); + } +}