diff --git a/README.md b/README.md index e5221a2..c2ef725 100644 --- a/README.md +++ b/README.md @@ -95,7 +95,10 @@ Is possible to configure the ACL for each action in the grid and the module conf Two steps are necessary to configure the retry for a queue: 1. Configure the dead letter exchange -2. Enable the message queue retry and declare the retry limit configuration +1. Declare the retry limit xml configuration +1. Enable the message queue retry admin configuration + +#### 1. Configuring the dead letter exchange Let's imagine a scenario that the `erp_order_export` queue already exists in your project and to simplify the example the topic name, exchange name and queue name are the same: `erp_order_export`. @@ -172,13 +175,29 @@ We added the `erp_order_export_delay` exchange and binding, it points to the ori The `erp_order_export_delay` queue does not have a consumer, it will be used only to hold(delay) messages according with the period defined in the `x-message-ttl` argument. -Now you have to define toggle the activation for the retry queue module and declare the retry limit for the queue: +#### 2. Declaring the retry limit xml configuration + +Create the `Vendor_ModuleName/etc/queue_retry.xml` file with the content: + +```xml + + + + +``` + +#### 3. Enabling the message queue retry admin configuration + +Now you have to toggle the activation for the retry queue module: System > Configuration > RUN-AS-ROOT > Message Queue Retry -![img.png](docs/configuration.png) +![img.png](docs/module-configuration.png) -**Important note:** Make sure to configure the retry limit of your queue in the module configuration. If you configure the dead letter exchange and do not set the retry limit in the configuration(System > Configuration > RUN-AS-ROOT > Message Queue Retry), the message will be in a retry loop, that is, execute until the consumer process the message without throwing an exception. This is the default behavior for the RabbitMQ dead letter exchange and will work this way even if this module is not installed. +**Important note:** Make sure to configure the retry limit of your queue with the `queue_retry.xml` file and enable the message queue retry configuration. +If you configure the dead letter exchange and do not do the steps mentioned, the message will be in a retry loop. In other words, it will execute until the consumer processes the message without throwing an exception. +This is the default behavior for the RabbitMQ dead letter exchange and will work this way even if this module is not installed. For more information of how to configure message queues in Magento 2, you can take a look [here](https://developer.adobe.com/commerce/php/development/components/message-queues/configuration/). diff --git a/docs/configuration.png b/docs/configuration.png deleted file mode 100644 index 14d7258..0000000 Binary files a/docs/configuration.png and /dev/null differ diff --git a/docs/module-configuration.png b/docs/module-configuration.png new file mode 100644 index 0000000..52d6e84 Binary files /dev/null and b/docs/module-configuration.png differ diff --git a/phpstan.neon.dist b/phpstan.neon.dist index c887a21..98a6ce2 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -5,8 +5,6 @@ parameters: excludePaths: analyseAndScan: - src/Test - ignoreErrors: - - '#Method .*construct\(\) has parameter \$data with no value type specified in iterable type array#' includes: - vendor/bitexpert/phpstan-magento/extension.neon diff --git a/src/Block/Adminhtml/QueuesConfig.php b/src/Block/Adminhtml/QueuesConfig.php deleted file mode 100644 index f4688cf..0000000 --- a/src/Block/Adminhtml/QueuesConfig.php +++ /dev/null @@ -1,27 +0,0 @@ -addColumn( - MessageQueueRetryConfig::MAIN_TOPIC_NAME, - [ 'label' => __('Main Topic Name'), 'class' => 'required-entry' ] - ); - - $this->addColumn( - MessageQueueRetryConfig::RETRY_LIMIT, - [ 'label' => __('Retry Limit'), 'class' => 'required-entry validate-zero-or-greater' ] - ); - - $this->_addAfter = false; - $this->_addButtonLabel = 'Add'; - } -} diff --git a/src/Config/QueueRetryConfigInterface.php b/src/Config/QueueRetryConfigInterface.php new file mode 100644 index 0000000..11337d4 --- /dev/null +++ b/src/Config/QueueRetryConfigInterface.php @@ -0,0 +1,15 @@ +>> + */ + public function convert($source): array + { + $topics = []; + + foreach ($source->getElementsByTagName('topic') as $topicNode) { + $topicAttributes = $topicNode->attributes; + $topicName = $topicAttributes->getNamedItem('name')?->nodeValue; + $retryLimit = (int)$topicAttributes->getNamedItem('retryLimit')?->nodeValue; + + $topics[$topicName] = [ + QueueRetryConfigInterface::TOPIC_NAME => $topicName, + QueueRetryConfigInterface::RETRY_LIMIT => $retryLimit, + ]; + } + + return [ QueueRetryConfigInterface::CONFIG_KEY_NAME => $topics ]; + } +} diff --git a/src/Model/Config/Backend/QueuesConfig.php b/src/Model/Config/Backend/QueuesConfig.php deleted file mode 100644 index f76b703..0000000 --- a/src/Model/Config/Backend/QueuesConfig.php +++ /dev/null @@ -1,59 +0,0 @@ - $value */ - $value = $this->getValue(); - - if (!is_array($value)) { - return parent::beforeSave(); - } - - $this->queueConfigurationValidator->validate($value); - - return parent::beforeSave(); - } -} diff --git a/src/Repository/Query/FindQueueRetryLimitByTopicNameQuery.php b/src/Repository/Query/FindQueueRetryLimitByTopicNameQuery.php new file mode 100644 index 0000000..48b05d3 --- /dev/null +++ b/src/Repository/Query/FindQueueRetryLimitByTopicNameQuery.php @@ -0,0 +1,28 @@ +configStorage->get($configKey); + + if (!$queueRetryTopic) { + return null; + } + + $retryLimitKey = QueueRetryConfigInterface::RETRY_LIMIT; + return isset($queueRetryTopic[$retryLimitKey]) ? (int)$queueRetryTopic[$retryLimitKey] : null; + } +} diff --git a/src/SchemaLocator/QueueRetrySchemaLocator.php b/src/SchemaLocator/QueueRetrySchemaLocator.php new file mode 100644 index 0000000..06c5d0f --- /dev/null +++ b/src/SchemaLocator/QueueRetrySchemaLocator.php @@ -0,0 +1,26 @@ +urnResolver->getRealPath(QueueRetryConfigInterface::XSD_FILE_URN); + } + + public function getPerFileSchema(): ?string + { + return $this->urnResolver->getRealPath(QueueRetryConfigInterface::XSD_FILE_URN); + } +} diff --git a/src/Service/IsMessageShouldBeSavedForRetryService.php b/src/Service/IsMessageShouldBeSavedForRetryService.php index e53fc5c..edfe657 100644 --- a/src/Service/IsMessageShouldBeSavedForRetryService.php +++ b/src/Service/IsMessageShouldBeSavedForRetryService.php @@ -4,21 +4,19 @@ namespace RunAsRoot\MessageQueueRetry\Service; -use JsonException; use Magento\Framework\MessageQueue\EnvelopeInterface; +use RunAsRoot\MessageQueueRetry\Repository\Query\FindQueueRetryLimitByTopicNameQuery; use RunAsRoot\MessageQueueRetry\System\Config\MessageQueueRetryConfig; class IsMessageShouldBeSavedForRetryService { public function __construct( private MessageQueueRetryConfig $messageQueueRetryConfig, - private GetMessageRetriesCountService $getMessageRetriesCountService + private GetMessageRetriesCountService $getMessageRetriesCountService, + private FindQueueRetryLimitByTopicNameQuery $findQueueRetryLimitByTopicNameQuery ) { } - /** - * @throws JsonException - */ public function execute(EnvelopeInterface $message): bool { if (!$this->messageQueueRetryConfig->isDelayQueueEnabled()) { @@ -38,24 +36,12 @@ public function execute(EnvelopeInterface $message): bool return false; } - $queueConfiguration = $this->getQueueConfiguration($topicName); + $retryLimit = $this->findQueueRetryLimitByTopicNameQuery->execute($topicName); - if (!$queueConfiguration) { + if ($retryLimit === null) { return false; } - $retryLimit = $queueConfiguration[MessageQueueRetryConfig::RETRY_LIMIT] ?? 0; - return $totalRetries >= $retryLimit; } - - /** - * @throws JsonException - * @return array|null - */ - private function getQueueConfiguration(string $topicName): ?array - { - $delayQueueConfiguration = $this->messageQueueRetryConfig->getDelayQueues(); - return $delayQueueConfiguration[$topicName] ?? null; - } } diff --git a/src/System/Config/MessageQueueRetryConfig.php b/src/System/Config/MessageQueueRetryConfig.php index 9d9bff6..651a02e 100644 --- a/src/System/Config/MessageQueueRetryConfig.php +++ b/src/System/Config/MessageQueueRetryConfig.php @@ -4,15 +4,10 @@ namespace RunAsRoot\MessageQueueRetry\System\Config; -use JsonException; use Magento\Framework\App\Config\ScopeConfigInterface; class MessageQueueRetryConfig { - public const MAIN_TOPIC_NAME = 'main_topic_name'; - public const DELAY_TOPIC_NAME = 'delay_topic_name'; - public const RETRY_LIMIT = 'retry_limit'; - private const XML_PATH_DELAY_QUEUES = 'message_queue_retry/general/delay_queues'; private const XML_PATH_ENABLE_DELAY_QUEUE = 'message_queue_retry/general/enable_delay_queue'; public function __construct(private ScopeConfigInterface $scopeConfig) @@ -23,33 +18,4 @@ public function isDelayQueueEnabled(): bool { return $this->scopeConfig->isSetFlag(self::XML_PATH_ENABLE_DELAY_QUEUE); } - - /** - * @return array> - * @throws JsonException - */ - public function getDelayQueues(): array - { - $configValues = $this->scopeConfig->getValue(self::XML_PATH_DELAY_QUEUES); - - if (!$configValues) { - return []; - } - - $configValues = json_decode($configValues, true, 512, JSON_THROW_ON_ERROR); - - $result = []; - - foreach ($configValues as $configValue) { - $mainTopicName = $configValue[self::MAIN_TOPIC_NAME] ?? null; - $retryLimit = isset($configValue[self::RETRY_LIMIT]) ? (int)$configValue[self::RETRY_LIMIT] : null; - $result[$mainTopicName] = [ - self::MAIN_TOPIC_NAME => $mainTopicName, - self::DELAY_TOPIC_NAME => $configValue[self::DELAY_TOPIC_NAME] ?? null, - self::RETRY_LIMIT => $retryLimit, - ]; - } - - return $result; - } } diff --git a/src/Test/Unit/Converter/QueueRetryXmlToArrayConverterTest.php b/src/Test/Unit/Converter/QueueRetryXmlToArrayConverterTest.php new file mode 100644 index 0000000..5d5a2ae --- /dev/null +++ b/src/Test/Unit/Converter/QueueRetryXmlToArrayConverterTest.php @@ -0,0 +1,44 @@ +sut = new QueueRetryXmlToArrayConverter(); + } + + public function testConvert(): void + { + $doc = new \DOMDocument(); + $doc->loadXML($this->getQueueRetryXmlFile()); + + $result = $this->sut->convert($doc); + + $expected = [ + 'queue_retry_topics' => [ + 'sample_topic' => [ + 'topic_name' => 'sample_topic', + 'retry_limit' => 3, + ], + 'another_topic' => [ + 'topic_name' => 'another_topic', + 'retry_limit' => 10, + ], + ], + ]; + + $this->assertEquals($expected, $result); + } + + public function getQueueRetryXmlFile(): string + { + return file_get_contents(__DIR__ . '/_files/queue_retry.xml'); + } +} diff --git a/src/Test/Unit/Converter/_files/queue_retry.xml b/src/Test/Unit/Converter/_files/queue_retry.xml new file mode 100644 index 0000000..17246ed --- /dev/null +++ b/src/Test/Unit/Converter/_files/queue_retry.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/src/Test/Unit/Model/Config/Backend/QueuesConfigTest.php b/src/Test/Unit/Model/Config/Backend/QueuesConfigTest.php deleted file mode 100644 index 1a1d3e9..0000000 --- a/src/Test/Unit/Model/Config/Backend/QueuesConfigTest.php +++ /dev/null @@ -1,54 +0,0 @@ -createMock(Context::class); - $eventManagerMock = $this->createMock(ManagerInterface::class); - $contextMock->expects($this->once())->method('getEventDispatcher')->willReturn($eventManagerMock); - $registryMock = $this->createMock(Registry::class); - $configMock = $this->createMock(ScopeConfigInterface::class); - $cacheTypeListMock = $this->createMock(TypeListInterface::class); - $abstractResourceMock = $this->createMock(AbstractResource::class); - $abstractDbMock = $this->createMock(AbstractDb::class); - $this->queueConfigurationValidatorMock = $this->createMock(QueueConfigurationValidator::class); - $this->sut = new QueuesConfig( - $contextMock, - $registryMock, - $configMock, - $cacheTypeListMock, - $this->queueConfigurationValidatorMock, - $abstractResourceMock, - $abstractDbMock - ); - } - - public function testBeforeSave(): void - { - $this->sut->setValue([]); - - $this->queueConfigurationValidatorMock->expects($this->once())->method('validate')->with([]); - - $this->sut->beforeSave(); - } -} diff --git a/src/Test/Unit/Repository/Query/FindQueueRetryLimitByTopicNameQueryTest.php b/src/Test/Unit/Repository/Query/FindQueueRetryLimitByTopicNameQueryTest.php new file mode 100644 index 0000000..99b3e59 --- /dev/null +++ b/src/Test/Unit/Repository/Query/FindQueueRetryLimitByTopicNameQueryTest.php @@ -0,0 +1,43 @@ +configStorageMock = $this->createMock(DataInterface::class); + $this->sut = new FindQueueRetryLimitByTopicNameQuery($this->configStorageMock); + } + + /** + * @dataProvider dataProvider + */ + public function testExecute($expected, $topicName, $config): void + { + $configKey = QueueRetryConfigInterface::CONFIG_KEY_NAME . '/' . $topicName; + $this->configStorageMock->expects($this->once())->method('get')->with($configKey)->willReturn($config); + + $result = $this->sut->execute($topicName); + + $this->assertEquals($expected, $result); + } + + public function dataProvider(): array + { + return [ + [3, 'sample_topic', ['retry_limit' => '3']], + [null, 'sample_topic', ['some_key' => '3']], + [null, 'sample_topic', null], + ]; + } +} diff --git a/src/Test/Unit/SchemaLocator/QueueRetrySchemaLocatorTest.php b/src/Test/Unit/SchemaLocator/QueueRetrySchemaLocatorTest.php new file mode 100644 index 0000000..076fcc4 --- /dev/null +++ b/src/Test/Unit/SchemaLocator/QueueRetrySchemaLocatorTest.php @@ -0,0 +1,43 @@ +urnResolverMock = $this->createMock(UrnResolver::class); + $this->sut = new QueueRetrySchemaLocator($this->urnResolverMock); + } + + public function testGetSchema(): void + { + $urn = QueueRetryConfigInterface::XSD_FILE_URN; + $realPath = 'some-path'; + $this->urnResolverMock->expects($this->once())->method('getRealPath')->with($urn)->willReturn($realPath); + + $result = $this->sut->getSchema(); + + $this->assertEquals($realPath, $result); + } + + public function testGetPerFileSchema(): void + { + $urn = QueueRetryConfigInterface::XSD_FILE_URN; + $realPath = 'some-path'; + $this->urnResolverMock->expects($this->once())->method('getRealPath')->with($urn)->willReturn($realPath); + + $result = $this->sut->getPerFileSchema(); + + $this->assertEquals($realPath, $result); + } +} diff --git a/src/Test/Unit/Service/GetMessageRetriesCountServiceTest.php b/src/Test/Unit/Service/GetMessageRetriesCountServiceTest.php index 9024f51..a430222 100644 --- a/src/Test/Unit/Service/GetMessageRetriesCountServiceTest.php +++ b/src/Test/Unit/Service/GetMessageRetriesCountServiceTest.php @@ -11,6 +11,13 @@ final class GetMessageRetriesCountServiceTest extends TestCase { + private GetMessageRetriesCountService $sut; + + protected function setUp(): void + { + $this->sut = new GetMessageRetriesCountService(); + } + public function testItGetsMessageRetriesCount(): void { $testRetriesCount = 3; @@ -21,10 +28,9 @@ public function testItGetsMessageRetriesCount(): void $messageProperties = ['application_headers' => $applicationHeaders, 'topic_name' => $topicName]; $messageMock->expects($this->once())->method('getProperties')->willReturn($messageProperties); - $sut = new GetMessageRetriesCountService(); - $retriesCount = $sut->execute($messageMock); + $retriesCount = $this->sut->execute($messageMock); - self::assertEquals($testRetriesCount, $retriesCount); + $this->assertEquals($testRetriesCount, $retriesCount); } public function testItReturnsZeroAfterFirstProcessingBecauseItIsNotRetry(): void @@ -33,9 +39,21 @@ public function testItReturnsZeroAfterFirstProcessingBecauseItIsNotRetry(): void $messageProperties = [ ]; $messageMock->expects($this->once())->method('getProperties')->willReturn($messageProperties); - $sut = new GetMessageRetriesCountService(); - $retriesCount = $sut->execute($messageMock); + $retriesCount = $this->sut->execute($messageMock); + + $this->assertEquals(0, $retriesCount); + } + + public function testItReturnsZeroAsFallback(): void + { + $messageMock = $this->createMock(Envelope::class); + $applicationHeaders = new AMQPTable(['x-death' => [['count' => null]]]); + $topicName = 'sample_topic'; + $messageProperties = ['application_headers' => $applicationHeaders, 'topic_name' => $topicName]; + $messageMock->expects($this->once())->method('getProperties')->willReturn($messageProperties); + + $retriesCount = $this->sut->execute($messageMock); - self::assertEquals(0, $retriesCount); + $this->assertEquals(0, $retriesCount); } } diff --git a/src/Test/Unit/Service/IsMessageShouldBeSavedForRetryServiceTest.php b/src/Test/Unit/Service/IsMessageShouldBeSavedForRetryServiceTest.php index e14dcbd..d39c752 100644 --- a/src/Test/Unit/Service/IsMessageShouldBeSavedForRetryServiceTest.php +++ b/src/Test/Unit/Service/IsMessageShouldBeSavedForRetryServiceTest.php @@ -7,36 +7,39 @@ use Magento\Framework\MessageQueue\Envelope; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; +use RunAsRoot\MessageQueueRetry\Repository\Query\FindQueueRetryLimitByTopicNameQuery; use RunAsRoot\MessageQueueRetry\Service\GetMessageRetriesCountService; use RunAsRoot\MessageQueueRetry\Service\IsMessageShouldBeSavedForRetryService; use RunAsRoot\MessageQueueRetry\System\Config\MessageQueueRetryConfig; final class IsMessageShouldBeSavedForRetryServiceTest extends TestCase { + private IsMessageShouldBeSavedForRetryService $sut; private MessageQueueRetryConfig|MockObject $messageQueueRetryConfigMock; private GetMessageRetriesCountService|MockObject $getMessageRetriesCountServiceMock; - private IsMessageShouldBeSavedForRetryService $sut; + private FindQueueRetryLimitByTopicNameQuery|MockObject $findQueueRetryLimitByTopicNameQueryMock; protected function setUp(): void { $this->messageQueueRetryConfigMock = $this->createMock(MessageQueueRetryConfig::class); $this->getMessageRetriesCountServiceMock = $this->createMock(GetMessageRetriesCountService::class); - - $this->messageQueueRetryConfigMock->method('isDelayQueueEnabled')->willReturn(true); + $this->findQueueRetryLimitByTopicNameQueryMock = $this->createMock(FindQueueRetryLimitByTopicNameQuery::class); $this->sut = new IsMessageShouldBeSavedForRetryService( $this->messageQueueRetryConfigMock, - $this->getMessageRetriesCountServiceMock + $this->getMessageRetriesCountServiceMock, + $this->findQueueRetryLimitByTopicNameQueryMock ); } public function testItReturnsTrueIfRetryLimitIsReached(): void { $testMessageProperties = ['topic_name' => 'sample_topic']; - $testQueueConfiguration = ['sample_topic' => ['retry_limit' => 2]]; + $this->messageQueueRetryConfigMock->method('isDelayQueueEnabled')->willReturn(true); $this->getMessageRetriesCountServiceMock->expects($this->once())->method('execute')->willReturn(2); - $this->messageQueueRetryConfigMock->expects($this->once())->method('getDelayQueues')->willReturn($testQueueConfiguration); + $this->findQueueRetryLimitByTopicNameQueryMock->expects($this->once())->method('execute') + ->with('sample_topic')->willReturn(2); $result = $this->sut->execute(new Envelope('', $testMessageProperties)); $this->assertTrue($result); @@ -45,10 +48,11 @@ public function testItReturnsTrueIfRetryLimitIsReached(): void public function testItReturnsFalseIfRetryLimitIsNotReached(): void { $testMessageProperties = ['topic_name' => 'sample_topic']; - $testQueueConfiguration = ['sample_topic' => ['retry_limit' => 2]]; + $this->messageQueueRetryConfigMock->method('isDelayQueueEnabled')->willReturn(true); $this->getMessageRetriesCountServiceMock->expects($this->once())->method('execute')->willReturn(1); - $this->messageQueueRetryConfigMock->expects($this->once())->method('getDelayQueues')->willReturn($testQueueConfiguration); + $this->findQueueRetryLimitByTopicNameQueryMock->expects($this->once())->method('execute') + ->with('sample_topic')->willReturn(2); $result = $this->sut->execute(new Envelope('', $testMessageProperties)); $this->assertFalse($result); @@ -57,22 +61,11 @@ public function testItReturnsFalseIfRetryLimitIsNotReached(): void public function testItReturnsFalseIfQueueConfigHasNoRetryLimit(): void { $testMessageProperties = ['topic_name' => 'sample_topic']; - $testQueueConfiguration = ['sample_topic' => []]; - - $this->getMessageRetriesCountServiceMock->expects($this->once())->method('execute')->willReturn(1); - $this->messageQueueRetryConfigMock->expects($this->once())->method('getDelayQueues')->willReturn($testQueueConfiguration); - - $result = $this->sut->execute(new Envelope('', $testMessageProperties)); - $this->assertFalse($result); - } - - public function testItReturnsFalseIfQueueIsNotConfigured(): void - { - $testMessageProperties = ['topic_name' => 'sample_topic']; - $testQueueConfiguration = ['another_topic' => ['retry_limit' => 1]]; + $this->messageQueueRetryConfigMock->method('isDelayQueueEnabled')->willReturn(true); $this->getMessageRetriesCountServiceMock->expects($this->once())->method('execute')->willReturn(1); - $this->messageQueueRetryConfigMock->expects($this->once())->method('getDelayQueues')->willReturn($testQueueConfiguration); + $this->findQueueRetryLimitByTopicNameQueryMock->expects($this->once())->method('execute') + ->with('sample_topic')->willReturn(null); $result = $this->sut->execute(new Envelope('', $testMessageProperties)); $this->assertFalse($result); @@ -80,6 +73,7 @@ public function testItReturnsFalseIfQueueIsNotConfigured(): void public function testItReturnsFalseIfMessageHasNoTopicName(): void { + $this->messageQueueRetryConfigMock->method('isDelayQueueEnabled')->willReturn(true); $this->getMessageRetriesCountServiceMock->expects($this->once())->method('execute')->willReturn(1); $result = $this->sut->execute(new Envelope('', [])); @@ -88,6 +82,7 @@ public function testItReturnsFalseIfMessageHasNoTopicName(): void public function testItReturnsFalseIfItIsFirstTimeConsuming(): void { + $this->messageQueueRetryConfigMock->method('isDelayQueueEnabled')->willReturn(true); $this->getMessageRetriesCountServiceMock->expects($this->once())->method('execute')->willReturn(0); $result = $this->sut->execute(new Envelope('', [])); @@ -96,9 +91,7 @@ public function testItReturnsFalseIfItIsFirstTimeConsuming(): void public function testItReturnsFalseIfConfigDisabled(): void { - $this->messageQueueRetryConfigMock->expects($this->once()) - ->method('isDelayQueueEnabled') - ->willReturn(false); + $this->messageQueueRetryConfigMock->expects($this->once())->method('isDelayQueueEnabled')->willReturn(false); $result = $this->sut->execute(new Envelope('', [])); $this->assertFalse($result); diff --git a/src/Test/Unit/System/Config/MessageQueueRetryConfigTest.php b/src/Test/Unit/System/Config/MessageQueueRetryConfigTest.php index 803825c..d55c348 100644 --- a/src/Test/Unit/System/Config/MessageQueueRetryConfigTest.php +++ b/src/Test/Unit/System/Config/MessageQueueRetryConfigTest.php @@ -24,28 +24,4 @@ public function testIsDelayQueueEnabled(): void $this->scopeConfigMock->expects($this->once())->method('isSetFlag')->with($configPath)->willReturn(true); $this->assertTrue($this->sut->isDelayQueueEnabled()); } - - public function testGetDelayQueues(): void - { - $configPath = 'message_queue_retry/general/delay_queues'; - $configValues = $this->getDelayQueuesJson(); - $this->scopeConfigMock->expects($this->once())->method('getValue')->with($configPath)->willReturn($configValues); - - $result = $this->sut->getDelayQueues(); - - $expected = [ - 'sample_topic' => [ - 'main_topic_name' => 'sample_topic', - 'delay_topic_name' => 'sample_topic_delay', - 'retry_limit' => 3, - ], - ]; - - $this->assertEquals($expected, $result); - } - - private function getDelayQueuesJson(): string - { - return trim(file_get_contents(__DIR__ . '/_files/delay_queues_config.json')); - } } diff --git a/src/Test/Unit/System/Config/_files/delay_queues_config.json b/src/Test/Unit/System/Config/_files/delay_queues_config.json deleted file mode 100644 index 506c03f..0000000 --- a/src/Test/Unit/System/Config/_files/delay_queues_config.json +++ /dev/null @@ -1 +0,0 @@ -{"_1677026917539_539":{"main_topic_name":"sample_topic","delay_topic_name":"sample_topic_delay","retry_limit":"3"}} diff --git a/src/Test/Unit/Validator/QueueConfigurationValidatorTest.php b/src/Test/Unit/Validator/QueueConfigurationValidatorTest.php deleted file mode 100644 index e517cee..0000000 --- a/src/Test/Unit/Validator/QueueConfigurationValidatorTest.php +++ /dev/null @@ -1,100 +0,0 @@ -sut = new QueueConfigurationValidator(); - } - - /** - * @dataProvider validConfigDataProvider - */ - public function testValidConfig(array $configValues): void - { - $this->assertTrue($this->sut->validate($configValues)); - } - - /** - * @dataProvider invalidConfigDataProvider - */ - public function testInvalidConfig(array $configValues): void - { - $this->expectException(InvalidQueueConfigurationException::class); - - $this->sut->validate($configValues); - } - - public function validConfigDataProvider(): array - { - return [ - [ - [ - [ - ConfigFieldNames::MAIN_TOPIC_NAME => 'topic1', - ConfigFieldNames::DELAY_TOPIC_NAME => 'topic2', - ], - [ - ConfigFieldNames::MAIN_TOPIC_NAME => 'topic3', - ConfigFieldNames::DELAY_TOPIC_NAME => null, - ], - [ - ConfigFieldNames::MAIN_TOPIC_NAME => null, - ConfigFieldNames::DELAY_TOPIC_NAME => 'topic4', - ], - [ - ConfigFieldNames::MAIN_TOPIC_NAME => null, - ConfigFieldNames::DELAY_TOPIC_NAME => null, - ], - ], - ], - ]; - } - - public function invalidConfigDataProvider(): array - { - return [ - [ - [ - [ - ConfigFieldNames::MAIN_TOPIC_NAME => 'topic1', - ConfigFieldNames::DELAY_TOPIC_NAME => 'topic2', - ], - [ - ConfigFieldNames::MAIN_TOPIC_NAME => 'topic1', - ConfigFieldNames::DELAY_TOPIC_NAME => 'topic3', - ], - ] - ], - [ - [ - [ - ConfigFieldNames::MAIN_TOPIC_NAME => 'topic1', - ConfigFieldNames::DELAY_TOPIC_NAME => 'topic2', - ], - [ - ConfigFieldNames::MAIN_TOPIC_NAME => 'topic3', - ConfigFieldNames::DELAY_TOPIC_NAME => 'topic2', - ], - ] - ], - [ - [ - [ - ConfigFieldNames::MAIN_TOPIC_NAME => 'topic1', - ConfigFieldNames::DELAY_TOPIC_NAME => 'topic1', - ], - ] - ], - ]; - } -} diff --git a/src/Validator/QueueConfigurationValidator.php b/src/Validator/QueueConfigurationValidator.php deleted file mode 100644 index d543bd7..0000000 --- a/src/Validator/QueueConfigurationValidator.php +++ /dev/null @@ -1,68 +0,0 @@ - $configValues - * @throws InvalidQueueConfigurationException - */ - public function validate(array $configValues): bool - { - $this->performUniqueValidation($configValues, ConfigFieldNames::MAIN_TOPIC_NAME, 'Main'); - $this->performUniqueValidation($configValues, ConfigFieldNames::DELAY_TOPIC_NAME, 'Delay'); - - foreach ($configValues as $configValue) { - $mainTopicName = $configValue[ConfigFieldNames::MAIN_TOPIC_NAME] ?? null; - $delayTopicName = $configValue[ConfigFieldNames::DELAY_TOPIC_NAME] ?? null; - - if ($mainTopicName === null && $delayTopicName === null) { - continue; - } - - if ($mainTopicName === $delayTopicName) { - throw new InvalidQueueConfigurationException( - new Phrase( - 'The main topic name "%1" and delay topic name "%2" cannot be the same.', - [ $mainTopicName, $delayTopicName ] - ) - ); - } - } - - return true; - } - - /** - * @param array $configValues - * @throws InvalidQueueConfigurationException - */ - private function performUniqueValidation(array $configValues, string $field, string $name): void - { - $topicNames = []; - - foreach ($configValues as $configValue) { - if (!isset($configValue[$field])) { - continue; - } - - if (in_array($configValue[$field], $topicNames)) { - throw new InvalidQueueConfigurationException( - new Phrase( - '%1 topic name "%2" is already used.', - [ $name, $configValue[$field] ] - ) - ); - } - - $topicNames[] = $configValue[$field]; - } - } -} diff --git a/src/etc/adminhtml/system.xml b/src/etc/adminhtml/system.xml index 77c4fa1..5715d31 100644 --- a/src/etc/adminhtml/system.xml +++ b/src/etc/adminhtml/system.xml @@ -22,16 +22,6 @@ default Magento behavior will run instead. - - - RunAsRoot\MessageQueueRetry\Block\Adminhtml\QueuesConfig - RunAsRoot\MessageQueueRetry\Model\Config\Backend\QueuesConfig - - Please, provide the topic name of the queue that is within the communication.xml file. - After the retry limit is reached the message will be sent to the failed messages grid. - - diff --git a/src/etc/di.xml b/src/etc/di.xml index f0bc341..c1e979c 100644 --- a/src/etc/di.xml +++ b/src/etc/di.xml @@ -2,7 +2,8 @@ - + @@ -24,4 +25,30 @@ + + + RunAsRootQueueRetryDataStorage + + + + + RunAsRootQueueRetryReader + + RunAsRoot\MessageQueueRetry\Config\QueueRetryConfigInterface::CACHE_KEY + + + + + + + RunAsRoot\MessageQueueRetry\Config\QueueRetryConfigInterface::FILE_NAME + + + RunAsRoot\MessageQueueRetry\Converter\QueueRetryXmlToArrayConverter + + + RunAsRoot\MessageQueueRetry\SchemaLocator\QueueRetrySchemaLocator + + + diff --git a/src/etc/queue_retry.xsd b/src/etc/queue_retry.xsd new file mode 100644 index 0000000..7c3a769 --- /dev/null +++ b/src/etc/queue_retry.xsd @@ -0,0 +1,33 @@ + + + + + retryLimit must contain value greater than or equal to 1 + + + + + + + + + + + + + + + + + + + + + Topic name must be unique. + + + + + + +