diff --git a/app/code/Magento/AsynchronousOperations/Model/ConfigInterface.php b/app/code/Magento/AsynchronousOperations/Model/ConfigInterface.php index 593ab52bbdf29..6e063fdb950db 100644 --- a/app/code/Magento/AsynchronousOperations/Model/ConfigInterface.php +++ b/app/code/Magento/AsynchronousOperations/Model/ConfigInterface.php @@ -30,6 +30,7 @@ interface ConfigInterface const SERVICE_PARAM_KEY_INTERFACE = 'interface'; const SERVICE_PARAM_KEY_METHOD = 'method'; const SERVICE_PARAM_KEY_TOPIC = 'topic'; + const SERVICE_PARAM_KEY_DESCRIPTION = 'description'; const DEFAULT_HANDLER_NAME = 'async'; const SYSTEM_TOPIC_NAME = 'async.system.required.wrapper.topic'; const SYSTEM_TOPIC_CONFIGURATION = [ @@ -48,7 +49,7 @@ interface ConfigInterface * @return array * @since 100.2.3 */ - public function getServices(); + public function getServices(): array; /** * Get topic name from webapi_async_config services config array by route url and http method @@ -59,5 +60,15 @@ public function getServices(); * @throws \Magento\Framework\Exception\LocalizedException * @since 100.2.3 */ - public function getTopicName($routeUrl, $httpMethod); + public function getTopicName(string $routeUrl, string $httpMethod): string; + + /** + * Get topic description from webapi_async_config services config array by route url and http method + * + * @param string $routeUrl + * @param string $httpMethod GET|POST|PUT|DELETE + * @return string + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function getTopicDescription(string $routeUrl, string $httpMethod): string; } diff --git a/app/code/Magento/AsynchronousOperations/Model/MassSchedule.php b/app/code/Magento/AsynchronousOperations/Model/MassSchedule.php index d8efed5562131..d664b47ac4641 100644 --- a/app/code/Magento/AsynchronousOperations/Model/MassSchedule.php +++ b/app/code/Magento/AsynchronousOperations/Model/MassSchedule.php @@ -112,15 +112,24 @@ public function __construct( * * @param string $topicName * @param array $entitiesArray + * @param string $topicDescription * @param string $groupId * @param string $userId * @return AsyncResponseInterface * @throws BulkException * @throws LocalizedException */ - public function publishMass($topicName, array $entitiesArray, $groupId = null, $userId = null) - { + public function publishMass( + $topicName, + array $entitiesArray, + $topicDescription = '', + $groupId = null, + $userId = null + ) { $bulkDescription = __('Topic %1', $topicName); + if ($topicDescription !== '') { + $bulkDescription = $topicDescription; + } if ($userId == null) { $userId = $this->userContext->getUserId(); diff --git a/app/code/Magento/Webapi/Model/Config/Converter.php b/app/code/Magento/Webapi/Model/Config/Converter.php index 837a0f84423ad..4d1010f0a2a2b 100644 --- a/app/code/Magento/Webapi/Model/Config/Converter.php +++ b/app/code/Magento/Webapi/Model/Config/Converter.php @@ -55,6 +55,10 @@ public function convert($source) $soapMethod = trim($soapOperationNode->nodeValue); } $url = trim($route->attributes->getNamedItem('url')->nodeValue); + $description = ''; + if ($descriptionNode = $route->attributes->getNamedItem('description')) { + $description = trim($descriptionNode->nodeValue); + } $version = $this->convertVersion($url); $serviceClassData = []; @@ -104,6 +108,7 @@ public function convert($source) ], self::KEY_ACL_RESOURCES => $resourceReferences, self::KEY_DATA_PARAMETERS => $data, + self::KEY_DESCRIPTION => $description ]; $serviceSecure = false; diff --git a/app/code/Magento/Webapi/Test/Unit/Model/Config/_files/webapi.php b/app/code/Magento/Webapi/Test/Unit/Model/Config/_files/webapi.php index bd8fcc78953a4..da468ee58e89d 100644 --- a/app/code/Magento/Webapi/Test/Unit/Model/Config/_files/webapi.php +++ b/app/code/Magento/Webapi/Test/Unit/Model/Config/_files/webapi.php @@ -70,6 +70,7 @@ 'value' => '%customer_id%', ], ], + 'description' => '' ], ], '/V1/customers/me' => [ @@ -88,6 +89,7 @@ 'value' => null, ], ], + 'description' => '' ], 'PUT' => [ 'secure' => true, @@ -104,6 +106,7 @@ 'value' => null, ], ], + 'description' => '' ], ], '/V1/customers' => [ @@ -118,6 +121,7 @@ ], 'parameters' => [ ], + 'description' => '' ], ], '/V1/customers/:id' => [ @@ -132,6 +136,7 @@ ], 'parameters' => [ ], + 'description' => '' ], 'DELETE' => [ 'secure' => false, @@ -145,6 +150,7 @@ ], 'parameters' => [ ], + 'description' => '' ], ], ], diff --git a/app/code/Magento/Webapi/etc/webapi_base.xsd b/app/code/Magento/Webapi/etc/webapi_base.xsd index 7d1a5a14ba78f..82844355e65fc 100644 --- a/app/code/Magento/Webapi/etc/webapi_base.xsd +++ b/app/code/Magento/Webapi/etc/webapi_base.xsd @@ -30,6 +30,7 @@ + diff --git a/app/code/Magento/WebapiAsync/Controller/Rest/AsynchronousRequestProcessor.php b/app/code/Magento/WebapiAsync/Controller/Rest/AsynchronousRequestProcessor.php index 81235e335b8fa..20b83647ab979 100644 --- a/app/code/Magento/WebapiAsync/Controller/Rest/AsynchronousRequestProcessor.php +++ b/app/code/Magento/WebapiAsync/Controller/Rest/AsynchronousRequestProcessor.php @@ -87,7 +87,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function process(\Magento\Framework\Webapi\Rest\Request $request) { @@ -99,11 +99,13 @@ public function process(\Magento\Framework\Webapi\Rest\Request $request) $entitiesParamsArray = $this->inputParamsResolver->resolve(); $topicName = $this->getTopicName($request); + $topicDescription = $this->getTopicDescription($request); try { $asyncResponse = $this->asyncBulkPublisher->publishMass( $topicName, - $entitiesParamsArray + $entitiesParamsArray, + $topicDescription ); } catch (BulkException $bulkException) { $asyncResponse = $bulkException->getData(); @@ -119,8 +121,11 @@ public function process(\Magento\Framework\Webapi\Rest\Request $request) } /** + * Get topic name from request + * * @param \Magento\Framework\Webapi\Rest\Request $request * @return string + * @throws \Magento\Framework\Exception\LocalizedException */ private function getTopicName($request) { @@ -133,7 +138,24 @@ private function getTopicName($request) } /** - * {@inheritdoc} + * Get topic description from request + * + * @param \Magento\Framework\Webapi\Rest\Request $request + * @return string + * @throws \Magento\Framework\Exception\LocalizedException + */ + private function getTopicDescription($request) + { + $route = $this->inputParamsResolver->getRoute(); + + return $this->webapiAsyncConfig->getTopicDescription( + $route->getRoutePath(), + $request->getHttpMethod() + ); + } + + /** + * @inheritdoc */ public function canProcess(\Magento\Framework\Webapi\Rest\Request $request) { @@ -148,6 +170,8 @@ public function canProcess(\Magento\Framework\Webapi\Rest\Request $request) } /** + * Check if path is bulk + * * @param \Magento\Framework\Webapi\Rest\Request $request * @return bool */ diff --git a/app/code/Magento/WebapiAsync/Model/Config.php b/app/code/Magento/WebapiAsync/Model/Config.php index 7329862ca528c..b20fe3f69082d 100644 --- a/app/code/Magento/WebapiAsync/Model/Config.php +++ b/app/code/Magento/WebapiAsync/Model/Config.php @@ -59,7 +59,7 @@ public function __construct( /** * @inheritdoc */ - public function getServices() + public function getServices(): array { if (null === $this->asyncServices) { $services = $this->cache->load(self::CACHE_ID); @@ -77,7 +77,29 @@ public function getServices() /** * @inheritdoc */ - public function getTopicName($routeUrl, $httpMethod) + public function getTopicName(string $routeUrl, string $httpMethod): string + { + return $this->getServiceParameter($routeUrl, $httpMethod, self::SERVICE_PARAM_KEY_TOPIC); + } + + /** + * @inheritdoc + */ + public function getTopicDescription(string $routeUrl, string $httpMethod): string + { + return $this->getServiceParameter($routeUrl, $httpMethod, self::SERVICE_PARAM_KEY_DESCRIPTION); + } + + /** + * Get service parameter by name + * + * @param string $routeUrl + * @param string $httpMethod + * @param string $attributeName + * @return string + * @throws LocalizedException + */ + private function getServiceParameter(string $routeUrl, string $httpMethod, string $attributeName): string { $services = $this->getServices(); $lookupKey = $this->generateLookupKeyByRouteData( @@ -91,7 +113,7 @@ public function getTopicName($routeUrl, $httpMethod) ); } - return $services[$lookupKey][self::SERVICE_PARAM_KEY_TOPIC]; + return $services[$lookupKey][$attributeName]; } /** @@ -101,7 +123,7 @@ public function getTopicName($routeUrl, $httpMethod) * * @return array */ - private function generateTopicsDataFromWebapiConfig() + private function generateTopicsDataFromWebapiConfig(): array { $webApiConfig = $this->webApiConfig->getServices(); $services = []; @@ -111,6 +133,11 @@ private function generateTopicsDataFromWebapiConfig() $serviceInterface = $httpMethodData[Converter::KEY_SERVICE][Converter::KEY_SERVICE_CLASS]; $serviceMethod = $httpMethodData[Converter::KEY_SERVICE][Converter::KEY_SERVICE_METHOD]; + $topicDescription = ''; + if (isset($httpMethodData[Converter::KEY_DESCRIPTION])) { + $topicDescription = $httpMethodData[Converter::KEY_DESCRIPTION]; + } + $lookupKey = $this->generateLookupKeyByRouteData( $routeUrl, $httpMethod @@ -123,9 +150,10 @@ private function generateTopicsDataFromWebapiConfig() ); $services[$lookupKey] = [ - self::SERVICE_PARAM_KEY_INTERFACE => $serviceInterface, - self::SERVICE_PARAM_KEY_METHOD => $serviceMethod, - self::SERVICE_PARAM_KEY_TOPIC => $topicName, + self::SERVICE_PARAM_KEY_INTERFACE => $serviceInterface, + self::SERVICE_PARAM_KEY_METHOD => $serviceMethod, + self::SERVICE_PARAM_KEY_TOPIC => $topicName, + self::SERVICE_PARAM_KEY_DESCRIPTION => $topicDescription ]; } } @@ -144,7 +172,7 @@ private function generateTopicsDataFromWebapiConfig() * @param string $httpMethod * @return string */ - private function generateLookupKeyByRouteData($routeUrl, $httpMethod) + private function generateLookupKeyByRouteData(string $routeUrl, string $httpMethod): string { return self::TOPIC_PREFIX . $this->generateKey($routeUrl, $httpMethod, '/', false); } @@ -161,7 +189,7 @@ private function generateLookupKeyByRouteData($routeUrl, $httpMethod) * @param string $httpMethod * @return string */ - private function generateTopicNameFromService($serviceInterface, $serviceMethod, $httpMethod) + private function generateTopicNameFromService(string $serviceInterface, string $serviceMethod, string $httpMethod): string { $typeName = strtolower(sprintf('%s.%s', $serviceInterface, $serviceMethod)); return strtolower(self::TOPIC_PREFIX . $this->generateKey($typeName, $httpMethod, '\\', false)); @@ -176,7 +204,7 @@ private function generateTopicNameFromService($serviceInterface, $serviceMethod, * @param bool $lcfirst * @return string */ - private function generateKey($typeName, $methodName, $delimiter = '\\', $lcfirst = true) + private function generateKey(string $typeName, string $methodName, string $delimiter = '\\', bool $lcfirst = true): string { $parts = explode($delimiter, trim($typeName, $delimiter)); foreach ($parts as &$part) { diff --git a/app/code/Magento/WebapiAsync/Test/Unit/Model/ConfigTest.php b/app/code/Magento/WebapiAsync/Test/Unit/Model/ConfigTest.php index df8367afe1775..007f7ee795e1d 100644 --- a/app/code/Magento/WebapiAsync/Test/Unit/Model/ConfigTest.php +++ b/app/code/Magento/WebapiAsync/Test/Unit/Model/ConfigTest.php @@ -67,7 +67,8 @@ public function testGetServicesSetsTopicFromServiceContractName() 'service' => [ 'class' => ProductRepositoryInterface::class, 'method' => 'save', - ] + ], + 'description' => '' ] ] ] diff --git a/dev/tests/integration/testsuite/Magento/AsynchronousOperations/Model/MassScheduleTest.php b/dev/tests/integration/testsuite/Magento/AsynchronousOperations/Model/MassScheduleTest.php index 4976c8098103b..d0ff64c883bd0 100644 --- a/dev/tests/integration/testsuite/Magento/AsynchronousOperations/Model/MassScheduleTest.php +++ b/dev/tests/integration/testsuite/Magento/AsynchronousOperations/Model/MassScheduleTest.php @@ -23,6 +23,8 @@ use Magento\Catalog\Model\ResourceModel\Product\Collection; use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Framework\ObjectManagerInterface; +use Magento\AsynchronousOperations\Model\ResourceModel\Bulk\Collection as BulkCollection; +use Magento\Webapi\Model\Config\Reader as ConfigReader; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -74,8 +76,15 @@ class MassScheduleTest extends \PHPUnit\Framework\TestCase */ private $registry; + /** @var \PHPUnit\Framework\MockObject\MockObject */ + protected $fileResolverMock; + + /** @var ConfigReader */ + protected $configReader; + protected function setUp(): void { + $this->fileResolverMock = $this->createMock(\Magento\Framework\Config\FileResolverInterface::class); $this->objectManager = Bootstrap::getObjectManager(); $this->registry = $this->objectManager->get(Registry::class); $this->massSchedule = $this->objectManager->create(MassSchedule::class); @@ -88,6 +97,10 @@ protected function setUp(): void 'logFilePath' => $this->logFilePath, 'appInitParams' => \Magento\TestFramework\Helper\Bootstrap::getInstance()->getAppInitParams() ]); + $this->configReader = $this->objectManager->create( + \Magento\Webapi\Model\Config\Reader::class, + ['fileResolver' => $this->fileResolverMock] + ); try { $this->publisherConsumerController->initialize(); @@ -126,6 +139,8 @@ public function testScheduleMass($products) public function sendBulk($products) { $this->skus = []; + $expectedDescription = 'Save Products Test Description'; + $description = $this->readDescription(); foreach ($products as $data) { if (isset($data['product'])) { $this->skus[] = $data['product']->getSku(); @@ -135,7 +150,8 @@ public function sendBulk($products) $result = $this->massSchedule->publishMass( 'async.magento.catalog.api.productrepositoryinterface.save.post', - $products + $products, + $description ); //assert bulk accepted with no errors @@ -143,6 +159,13 @@ public function sendBulk($products) //assert number of products sent to queue $this->assertCount(count($this->skus), $result->getRequestItems()); + + //assert topic description + $this->assertEquals( + $expectedDescription, + $this->getDescription($result->getBulkUuid()), + 'Description is wrong' + ); } protected function tearDown(): void @@ -194,6 +217,18 @@ public function assertProductExists($productsSkus, $count) return $size == $count; } + public function getDescription($uuid) + { + $bulkDescription = ''; + $collection = $this->objectManager->create(BulkCollection::class) + ->addFieldToFilter('uuid', ['in' => $uuid]) + ->load(); + if (!empty($collection->getFirstItem()->getData())) { + $bulkDescription = $collection->getFirstItem()->getDescription(); + } + return $bulkDescription; + } + /** * @dataProvider productExceptionDataProvider * @param ProductInterface[] $products @@ -305,4 +340,14 @@ public function productExceptionDataProvider() ], ]; } + + public function readDescription() + { + $configFiles = [ + file_get_contents(realpath(__DIR__ . '/../_files/webapiA.xml')) + ]; + $this->fileResolverMock->expects($this->any())->method('get')->will($this->returnValue($configFiles)); + $value = $this->configReader->read(); + return $value['routes']['/V1/products']['POST']['description']; + } } diff --git a/dev/tests/integration/testsuite/Magento/AsynchronousOperations/_files/webapiA.xml b/dev/tests/integration/testsuite/Magento/AsynchronousOperations/_files/webapiA.xml new file mode 100644 index 0000000000000..6ec8bdae8809d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/AsynchronousOperations/_files/webapiA.xml @@ -0,0 +1,16 @@ + + + + + + + + + + diff --git a/dev/tests/integration/testsuite/Magento/Webapi/Model/Config/_files/webapi.php b/dev/tests/integration/testsuite/Magento/Webapi/Model/Config/_files/webapi.php index 57c8fbf45c63c..232b92ac36700 100644 --- a/dev/tests/integration/testsuite/Magento/Webapi/Model/Config/_files/webapi.php +++ b/dev/tests/integration/testsuite/Magento/Webapi/Model/Config/_files/webapi.php @@ -138,6 +138,7 @@ ], 'parameters' => [ ], + 'description' => '' ], ], '/V1/testmoduleMSC' => [ @@ -152,6 +153,7 @@ ], 'parameters' => [ ], + 'description' => '' ], ], '/V1/testmodule1/:id' => [ @@ -166,6 +168,7 @@ ], 'parameters' => [ ], + 'description' => '' ], ], '/V1/testmodule1' => [ @@ -184,6 +187,7 @@ 'value' => null, ], ], + 'description' => '' ], 'POST' => [ 'secure' => false, @@ -200,6 +204,7 @@ 'value' => null, ], ], + 'description' => '' ], ], '/V2/testmodule1/:id' => [ @@ -215,6 +220,7 @@ ], 'parameters' => [ ], + 'description' => '' ], 'DELETE' => [ 'secure' => false, @@ -228,6 +234,7 @@ ], 'parameters' => [ ], + 'description' => '' ], 'PUT' => [ 'secure' => false, @@ -241,6 +248,7 @@ ], 'parameters' => [ ], + 'description' => '' ], ], '/V2/testmodule1' => [ @@ -260,6 +268,7 @@ 'value' => null, ], ], + 'description' => '' ], ], '/V2/testmoduleMSC/itemPreconfigured' => [ @@ -274,6 +283,7 @@ 'Magento_TestModuleMSC::resource2' => true, ], 'parameters' => [], + 'description' => '' ] ] ],