diff --git a/.travis.yml b/.travis.yml index 8ebebda1c6adf..e64b01e61c857 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,8 +16,8 @@ services: - elasticsearch language: php php: - - 7.0 - 7.1 + - 7.2 env: global: - COMPOSER_BIN_DIR=~/bin @@ -35,13 +35,13 @@ env: - TEST_SUITE=functional matrix: exclude: - - php: 7.0 + - php: 7.1 env: TEST_SUITE=static - - php: 7.0 + - php: 7.1 env: TEST_SUITE=js GRUNT_COMMAND=spec - - php: 7.0 + - php: 7.1 env: TEST_SUITE=js GRUNT_COMMAND=static - - php: 7.0 + - php: 7.1 env: TEST_SUITE=functional cache: apt: true diff --git a/README.md b/README.md index b20a3738ba775..a2cf536bb6520 100644 --- a/README.md +++ b/README.md @@ -3,16 +3,15 @@ [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/magento/magento2?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [![Crowdin](https://d322cqt584bo4o.cloudfront.net/magento-2/localized.png)](https://crowdin.com/project/magento-2)

Welcome

-Welcome to Magento 2 installation! We're glad you chose to install Magento 2, a cutting edge, feature-rich eCommerce solution that gets results. +Welcome to Magento 2 installation! We're glad you chose to install Magento 2, a cutting-edge, feature-rich eCommerce solution that gets results. ## Magento system requirements -[Magento system requirements](http://devdocs.magento.com/guides/v2.2/install-gde/system-requirements2.html) +[Magento system requirements](http://devdocs.magento.com/guides/v2.3/install-gde/system-requirements2.html) ## Install Magento To install Magento, see either: -* [Magento DevBox](https://magento.com/tech-resources/download), the easiest way to get started with Magento. -* [Installation guide](http://devdocs.magento.com/guides/v2.2/install-gde/bk-install-guide.html) +* [Installation guide](http://devdocs.magento.com/guides/v2.3/install-gde/bk-install-guide.html)

Contributing to the Magento 2 code base

Contributions can take the form of new components or features, changes to existing features, tests, documentation (such as developer guides, user guides, examples, or specifications), bug fixes, optimizations, or just good suggestions. @@ -23,11 +22,24 @@ To learn about issues, click [here][2]. To open an issue, click [here][3]. To suggest documentation improvements, click [here][4]. -[1]: -[2]: +[1]: +[2]: [3]: [4]: +

Community Maintainers

+The members of this team have been recognized for their outstanding commitment to maintaining and improving Magento. Magento has granted them permission to accept, merge, and reject pull requests, as well as review issues, and thanks these Community Maintainers for their valuable contributions. + + + + + +

Top Contributors

+Magento is thankful for any contribution that can improve our code base, documentation or increase test coverage. We always recognize our most active members, as their contributions are the foundation of the Magento Open Source platform. + + + +

Labels applied by the Magento team

| Label | Description | diff --git a/app/code/Magento/AdminNotification/Block/System/Messages.php b/app/code/Magento/AdminNotification/Block/System/Messages.php index e95d68663bf04..b950f5583e599 100644 --- a/app/code/Magento/AdminNotification/Block/System/Messages.php +++ b/app/code/Magento/AdminNotification/Block/System/Messages.php @@ -16,24 +16,34 @@ class Messages extends \Magento\Backend\Block\Template /** * @var \Magento\Framework\Json\Helper\Data + * @deprecated */ protected $jsonHelper; + /** + * @var \Magento\Framework\Serialize\Serializer\Json + */ + private $serializer; + /** * @param \Magento\Backend\Block\Template\Context $context * @param \Magento\AdminNotification\Model\ResourceModel\System\Message\Collection\Synchronized $messages * @param \Magento\Framework\Json\Helper\Data $jsonHelper * @param array $data + * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer */ public function __construct( \Magento\Backend\Block\Template\Context $context, \Magento\AdminNotification\Model\ResourceModel\System\Message\Collection\Synchronized $messages, \Magento\Framework\Json\Helper\Data $jsonHelper, - array $data = [] + array $data = [], + \Magento\Framework\Serialize\Serializer\Json $serializer = null ) { $this->jsonHelper = $jsonHelper; parent::__construct($context, $data); $this->_messages = $messages; + $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Framework\Serialize\Serializer\Json::class); } /** @@ -117,7 +127,7 @@ protected function _getMessagesUrl() */ public function getSystemMessageDialogJson() { - return $this->jsonHelper->jsonEncode( + return $this->serializer->serialize( [ 'systemMessageDialog' => [ 'buttons' => [], diff --git a/app/code/Magento/AdminNotification/Block/System/Messages/UnreadMessagePopup.php b/app/code/Magento/AdminNotification/Block/System/Messages/UnreadMessagePopup.php index 7ea0062581467..2d4c7f279f707 100644 --- a/app/code/Magento/AdminNotification/Block/System/Messages/UnreadMessagePopup.php +++ b/app/code/Magento/AdminNotification/Block/System/Messages/UnreadMessagePopup.php @@ -77,9 +77,8 @@ public function getPopupTitle() $messageCount = count($this->_messages->getUnread()); if ($messageCount > 1) { return __('You have %1 new system messages', $messageCount); - } else { - return __('You have %1 new system message', $messageCount); } + return __('You have %1 new system message', $messageCount); } /** diff --git a/app/code/Magento/AdminNotification/Block/Window.php b/app/code/Magento/AdminNotification/Block/Window.php index b80e12a8674db..9563626ee2577 100644 --- a/app/code/Magento/AdminNotification/Block/Window.php +++ b/app/code/Magento/AdminNotification/Block/Window.php @@ -98,10 +98,9 @@ protected function _getLatestItem() { if ($this->_latestItem == null) { $items = array_values($this->_criticalCollection->getItems()); + $this->_latestItem = false; if (count($items)) { $this->_latestItem = $items[0]; - } else { - $this->_latestItem = false; } } return $this->_latestItem; diff --git a/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/MarkAsRead.php b/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/MarkAsRead.php index 79f69ab5da88d..6b5e0681139cf 100644 --- a/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/MarkAsRead.php +++ b/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/MarkAsRead.php @@ -28,11 +28,11 @@ public function execute() )->markAsRead( $notificationId ); - $this->messageManager->addSuccess(__('The message has been marked as Read.')); + $this->messageManager->addSuccessMessage(__('The message has been marked as Read.')); } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addException( + $this->messageManager->addExceptionMessage( $e, __("We couldn't mark the notification as Read because of an error.") ); diff --git a/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/MassMarkAsRead.php b/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/MassMarkAsRead.php index 9e61b8ff4b83c..9ae4a7cdac0b9 100644 --- a/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/MassMarkAsRead.php +++ b/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/MassMarkAsRead.php @@ -23,7 +23,7 @@ public function execute() { $ids = $this->getRequest()->getParam('notification'); if (!is_array($ids)) { - $this->messageManager->addError(__('Please select messages.')); + $this->messageManager->addErrorMessage(__('Please select messages.')); } else { try { foreach ($ids as $id) { @@ -32,13 +32,13 @@ public function execute() $model->setIsRead(1)->save(); } } - $this->messageManager->addSuccess( + $this->messageManager->addSuccessMessage( __('A total of %1 record(s) have been marked as Read.', count($ids)) ); } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addException( + $this->messageManager->addExceptionMessage( $e, __("We couldn't mark the notification as Read because of an error.") ); diff --git a/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/MassRemove.php b/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/MassRemove.php index 94c7d955f592b..06659b8452cab 100644 --- a/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/MassRemove.php +++ b/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/MassRemove.php @@ -23,7 +23,7 @@ public function execute() { $ids = $this->getRequest()->getParam('notification'); if (!is_array($ids)) { - $this->messageManager->addError(__('Please select messages.')); + $this->messageManager->addErrorMessage(__('Please select messages.')); } else { try { foreach ($ids as $id) { @@ -32,11 +32,14 @@ public function execute() $model->setIsRemove(1)->save(); } } - $this->messageManager->addSuccess(__('Total of %1 record(s) have been removed.', count($ids))); + $this->messageManager->addSuccessMessage(__('Total of %1 record(s) have been removed.', count($ids))); } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addException($e, __("We couldn't remove the messages because of an error.")); + $this->messageManager->addExceptionMessage( + $e, + __("We couldn't remove the messages because of an error.") + ); } } $this->_redirect('adminhtml/*/'); diff --git a/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/Remove.php b/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/Remove.php index 17f911339cb61..f0724a9587c50 100644 --- a/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/Remove.php +++ b/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/Remove.php @@ -31,11 +31,14 @@ public function execute() try { $model->setIsRemove(1)->save(); - $this->messageManager->addSuccess(__('The message has been removed.')); + $this->messageManager->addSuccessMessage(__('The message has been removed.')); } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addException($e, __("We couldn't remove the messages because of an error.")); + $this->messageManager->addExceptionMessage( + $e, + __("We couldn't remove the messages because of an error.") + ); } $this->_redirect('adminhtml/*/'); diff --git a/app/code/Magento/AdminNotification/Controller/Adminhtml/System/Message/ListAction.php b/app/code/Magento/AdminNotification/Controller/Adminhtml/System/Message/ListAction.php index c332440276083..303675b968256 100644 --- a/app/code/Magento/AdminNotification/Controller/Adminhtml/System/Message/ListAction.php +++ b/app/code/Magento/AdminNotification/Controller/Adminhtml/System/Message/ListAction.php @@ -6,6 +6,8 @@ */ namespace Magento\AdminNotification\Controller\Adminhtml\System\Message; +use Magento\Framework\Controller\ResultFactory; + class ListAction extends \Magento\Backend\App\AbstractAction { /** @@ -15,6 +17,7 @@ class ListAction extends \Magento\Backend\App\AbstractAction /** * @var \Magento\Framework\Json\Helper\Data + * @deprecated */ protected $jsonHelper; @@ -41,7 +44,7 @@ public function __construct( } /** - * @return void + * @return \Magento\Framework\Controller\Result\Json */ public function execute() { @@ -63,6 +66,9 @@ public function execute() . 'Please refresh the web page to clear the notice alert.', ]; } - $this->getResponse()->representJson($this->jsonHelper->jsonEncode($result)); + /** @var \Magento\Framework\Controller\Result\Json $resultJson */ + $resultJson = $this->resultFactory->create(ResultFactory::TYPE_JSON); + $resultJson->setData($result); + return $resultJson; } } diff --git a/app/code/Magento/AdminNotification/composer.json b/app/code/Magento/AdminNotification/composer.json index b8dba6f899645..e5cf487908cd7 100644 --- a/app/code/Magento/AdminNotification/composer.json +++ b/app/code/Magento/AdminNotification/composer.json @@ -5,16 +5,15 @@ "sort-packages": true }, "require": { - "php": "7.0.2|7.0.4|~7.0.6|~7.1.0", + "php": "~7.1.3||~7.2.0", "lib-libxml": "*", - "magento/framework": "100.3.*", - "magento/module-backend": "100.3.*", - "magento/module-media-storage": "100.3.*", - "magento/module-store": "100.3.*", - "magento/module-ui": "100.3.*" + "magento/framework": "*", + "magento/module-backend": "*", + "magento/module-media-storage": "*", + "magento/module-store": "*", + "magento/module-ui": "*" }, "type": "magento2-module", - "version": "100.3.0-dev", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/AdvancedPricingImportExport/Model/Export/AdvancedPricing.php b/app/code/Magento/AdvancedPricingImportExport/Model/Export/AdvancedPricing.php index 7ddd5e3bb2a36..a92df095036f3 100644 --- a/app/code/Magento/AdvancedPricingImportExport/Model/Export/AdvancedPricing.php +++ b/app/code/Magento/AdvancedPricingImportExport/Model/Export/AdvancedPricing.php @@ -5,6 +5,7 @@ */ namespace Magento\AdvancedPricingImportExport\Model\Export; +use Magento\ImportExport\Model\Export; use Magento\Store\Model\Store; use Magento\CatalogImportExport\Model\Import\Product as ImportProduct; use Magento\AdvancedPricingImportExport\Model\Import\AdvancedPricing as ImportAdvancedPricing; @@ -79,6 +80,11 @@ class AdvancedPricing extends \Magento\CatalogImportExport\Model\Export\Product ImportAdvancedPricing::COL_TIER_PRICE_TYPE => '' ]; + /** + * @var string[] + */ + private $websiteCodesMap = []; + /** * @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate * @param \Magento\Eav\Model\Config $config @@ -255,36 +261,131 @@ public function filterAttributeCollection(\Magento\Eav\Model\ResourceModel\Entit */ protected function getExportData() { + if ($this->_passTierPrice) { + return []; + } + $exportData = []; try { - $rawData = $this->collectRawData(); - $productIds = array_keys($rawData); - if (isset($productIds)) { - if (!$this->_passTierPrice) { - $exportData = array_merge( - $exportData, - $this->getTierPrices($productIds, ImportAdvancedPricing::TABLE_TIER_PRICE) - ); + $productsByStores = $this->loadCollection(); + if (!empty($productsByStores)) { + $linkField = $this->getProductEntityLinkField(); + $productLinkIds = []; + + foreach ($productsByStores as $product) { + $productLinkIds[array_pop($product)[$linkField]] = true; + } + $productLinkIds = array_keys($productLinkIds); + $tierPricesData = $this->fetchTierPrices($productLinkIds); + $exportData = $this->prepareExportData( + $productsByStores, + $tierPricesData + ); + if (!empty($exportData)) { + asort($exportData); } } - if ($exportData) { - $exportData = $this->correctExportData($exportData); - } - if (isset($exportData)) { - asort($exportData); - } - } catch (\Exception $e) { + } catch (\Throwable $e) { $this->_logger->critical($e); } + return $exportData; } + /** + * Creating export-formatted row from tier price. + * + * @param array $tierPriceData Tier price information. + * + * @return array Formatted for export tier price information. + */ + private function createExportRow(array $tierPriceData): array + { + //List of columns to display in export row. + $exportRow = $this->templateExportData; + + foreach (array_keys($exportRow) as $keyTemplate) { + if (array_key_exists($keyTemplate, $tierPriceData)) { + if (in_array($keyTemplate, $this->_priceWebsite)) { + //If it's website column then getting website code. + $exportRow[$keyTemplate] = $this->_getWebsiteCode( + $tierPriceData[$keyTemplate] + ); + } elseif (in_array($keyTemplate, $this->_priceCustomerGroup)) { + //If it's customer group column then getting customer + //group name by ID. + $exportRow[$keyTemplate] = $this->_getCustomerGroupById( + $tierPriceData[$keyTemplate], + $tierPriceData[ImportAdvancedPricing::VALUE_ALL_GROUPS] + ); + unset($exportRow[ImportAdvancedPricing::VALUE_ALL_GROUPS]); + } elseif ($keyTemplate + === ImportAdvancedPricing::COL_TIER_PRICE + ) { + //If it's price column then getting value and type + //of tier price. + $exportRow[$keyTemplate] + = $tierPriceData[ImportAdvancedPricing::COL_TIER_PRICE_PERCENTAGE_VALUE] + ? $tierPriceData[ImportAdvancedPricing::COL_TIER_PRICE_PERCENTAGE_VALUE] + : $tierPriceData[ImportAdvancedPricing::COL_TIER_PRICE]; + $exportRow[ImportAdvancedPricing::COL_TIER_PRICE_TYPE] + = $this->tierPriceTypeValue($tierPriceData); + } else { + //Any other column just goes as is. + $exportRow[$keyTemplate] = $tierPriceData[$keyTemplate]; + } + } + } + + return $exportRow; + } + + /** + * Prepare data for export. + * + * @param array $productsData Products to export. + * @param array $tierPricesData Their tier prices. + * + * @return array Export rows to display. + */ + private function prepareExportData( + array $productsData, + array $tierPricesData + ): array { + //Assigning SKUs to tier prices data. + $productLinkIdToSkuMap = []; + foreach ($productsData as $productData) { + $productLinkIdToSkuMap[$productData[Store::DEFAULT_STORE_ID][$this->getProductEntityLinkField()]] + = $productData[Store::DEFAULT_STORE_ID]['sku']; + } + + //Adding products' SKUs to tier price data. + $linkedTierPricesData = []; + foreach ($tierPricesData as $tierPriceData) { + $sku = $productLinkIdToSkuMap[$tierPriceData['product_link_id']]; + $linkedTierPricesData[] = array_merge( + $tierPriceData, + [ImportAdvancedPricing::COL_SKU => $sku] + ); + } + + //Formatting data for export. + $customExportData = []; + foreach ($linkedTierPricesData as $row) { + $customExportData[] = $this->createExportRow($row); + } + + return $customExportData; + } + /** * Correct export data. * * @param array $exportData * @return array * @SuppressWarnings(PHPMD.UnusedLocalVariable) + * @deprecated + * @see prepareExportData */ protected function correctExportData($exportData) { @@ -327,16 +428,83 @@ protected function correctExportData($exportData) /** * Check type for tier price. * - * @param string $tierPricePercentage + * @param array $tierPriceData * @return string */ - private function tierPriceTypeValue($tierPricePercentage) + private function tierPriceTypeValue(array $tierPriceData): string { - return $tierPricePercentage + return $tierPriceData[ImportAdvancedPricing::COL_TIER_PRICE_PERCENTAGE_VALUE] ? ImportAdvancedPricing::TIER_PRICE_TYPE_PERCENT : ImportAdvancedPricing::TIER_PRICE_TYPE_FIXED; } + /** + * Load tier prices for given products. + * + * @param string[] $productIds Link IDs of products to find tier prices for. + * + * @return array Tier prices data. + * + * @SuppressWarnings(PHPMD.NPathComplexity) + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + */ + private function fetchTierPrices(array $productIds): array + { + if (empty($productIds)) { + throw new \InvalidArgumentException( + 'Can only load tier prices for specific products' + ); + } + + $pricesTable = ImportAdvancedPricing::TABLE_TIER_PRICE; + $exportFilter = null; + $priceFromFilter = null; + $priceToFilter = null; + if (isset($this->_parameters[Export::FILTER_ELEMENT_GROUP])) { + $exportFilter = $this->_parameters[Export::FILTER_ELEMENT_GROUP]; + } + $productEntityLinkField = $this->getProductEntityLinkField(); + $selectFields = [ + ImportAdvancedPricing::COL_TIER_PRICE_WEBSITE => 'ap.website_id', + ImportAdvancedPricing::VALUE_ALL_GROUPS => 'ap.all_groups', + ImportAdvancedPricing::COL_TIER_PRICE_CUSTOMER_GROUP => 'ap.customer_group_id', + ImportAdvancedPricing::COL_TIER_PRICE_QTY => 'ap.qty', + ImportAdvancedPricing::COL_TIER_PRICE => 'ap.value', + ImportAdvancedPricing::COL_TIER_PRICE_PERCENTAGE_VALUE => 'ap.percentage_value', + 'product_link_id' => 'ap.' .$productEntityLinkField, + ]; + if ($exportFilter && array_key_exists('tier_price', $exportFilter)) { + if (!empty($exportFilter['tier_price'][0])) { + $priceFromFilter = $exportFilter['tier_price'][0]; + } + if (!empty($exportFilter['tier_price'][1])) { + $priceToFilter = $exportFilter['tier_price'][1]; + } + } + + $select = $this->_connection->select() + ->from( + ['ap' => $this->_resource->getTableName($pricesTable)], + $selectFields + ) + ->where( + 'ap.'.$productEntityLinkField.' IN (?)', + $productIds + ); + + if ($priceFromFilter !== null) { + $select->where('ap.value >= ?', $priceFromFilter); + } + if ($priceToFilter !== null) { + $select->where('ap.value <= ?', $priceToFilter); + } + if ($priceFromFilter || $priceToFilter) { + $select->orWhere('ap.percentage_value IS NOT NULL'); + } + + return $this->_connection->fetchAll($select); + } + /** * Get tier prices. * @@ -345,6 +513,8 @@ private function tierPriceTypeValue($tierPricePercentage) * @return array|bool * @SuppressWarnings(PHPMD.NPathComplexity) * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @deprecated + * @see fetchTierPrices */ protected function getTierPrices(array $listSku, $table) { @@ -413,41 +583,51 @@ protected function getTierPrices(array $listSku, $table) } /** - * Get Website code + * Get Website code. * * @param int $websiteId + * * @return string */ - protected function _getWebsiteCode($websiteId) + protected function _getWebsiteCode(int $websiteId): string { - $storeName = ($websiteId == 0) - ? ImportAdvancedPricing::VALUE_ALL_WEBSITES - : $this->_storeManager->getWebsite($websiteId)->getCode(); - $currencyCode = ''; - if ($websiteId == 0) { - $currencyCode = $this->_storeManager->getWebsite($websiteId)->getBaseCurrencyCode(); - } - if ($storeName && $currencyCode) { - return $storeName . ' [' . $currencyCode . ']'; - } else { - return $storeName; + if (!array_key_exists($websiteId, $this->websiteCodesMap)) { + $storeName = ($websiteId == 0) + ? ImportAdvancedPricing::VALUE_ALL_WEBSITES + : $this->_storeManager->getWebsite($websiteId)->getCode(); + $currencyCode = ''; + if ($websiteId == 0) { + $currencyCode = $this->_storeManager->getWebsite($websiteId) + ->getBaseCurrencyCode(); + } + + if ($storeName && $currencyCode) { + $code = $storeName.' ['.$currencyCode.']'; + } else { + $code = $storeName; + } + $this->websiteCodesMap[$websiteId] = $code; } + + return $this->websiteCodesMap[$websiteId]; } /** - * Get Customer Group By Id + * Get Customer Group By Id. + * + * @param int $groupId + * @param int $allGroups * - * @param int $customerGroupId - * @param null $allGroups * @return string */ - protected function _getCustomerGroupById($customerGroupId, $allGroups = null) - { - if ($allGroups) { + protected function _getCustomerGroupById( + int $groupId, + int $allGroups = 0 + ): string { + if ($allGroups !== 0) { return ImportAdvancedPricing::VALUE_ALL_GROUPS; - } else { - return $this->_groupRepository->getById($customerGroupId)->getCode(); } + return $this->_groupRepository->getById($groupId)->getCode(); } /** diff --git a/app/code/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricing.php b/app/code/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricing.php index 0e8acb37104e6..4663aea7a7dfc 100644 --- a/app/code/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricing.php +++ b/app/code/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricing.php @@ -482,9 +482,8 @@ protected function deleteProductTierPrices(array $listSku, $table) $this->addRowError(ValidatorInterface::ERROR_SKU_IS_EMPTY, 0); return false; } - } else { - return false; } + return false; } /** diff --git a/app/code/Magento/AdvancedPricingImportExport/composer.json b/app/code/Magento/AdvancedPricingImportExport/composer.json index 1660104953504..12e1d9938f4bd 100644 --- a/app/code/Magento/AdvancedPricingImportExport/composer.json +++ b/app/code/Magento/AdvancedPricingImportExport/composer.json @@ -5,18 +5,17 @@ "sort-packages": true }, "require": { - "php": "7.0.2|7.0.4|~7.0.6|~7.1.0", - "magento/framework": "100.3.*", - "magento/module-catalog": "101.2.*", - "magento/module-catalog-import-export": "100.3.*", - "magento/module-catalog-inventory": "100.3.*", - "magento/module-customer": "100.3.*", - "magento/module-eav": "100.3.*", - "magento/module-import-export": "100.3.*", - "magento/module-store": "100.3.*" + "php": "~7.1.3||~7.2.0", + "magento/framework": "*", + "magento/module-catalog": "*", + "magento/module-catalog-import-export": "*", + "magento/module-catalog-inventory": "*", + "magento/module-customer": "*", + "magento/module-eav": "*", + "magento/module-import-export": "*", + "magento/module-store": "*" }, "type": "magento2-module", - "version": "100.3.0-dev", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/AdvancedSearch/composer.json b/app/code/Magento/AdvancedSearch/composer.json index 2d549c12235db..a224a1001cd01 100644 --- a/app/code/Magento/AdvancedSearch/composer.json +++ b/app/code/Magento/AdvancedSearch/composer.json @@ -5,18 +5,17 @@ "sort-packages": true }, "require": { - "magento/framework": "100.3.*", - "magento/module-backend": "100.3.*", - "magento/module-catalog": "101.2.*", - "magento/module-catalog-search": "100.3.*", - "magento/module-config": "100.3.*", - "magento/module-customer": "100.3.*", - "magento/module-search": "100.3.*", - "magento/module-store": "100.3.*", - "php": "7.0.2|7.0.4|~7.0.6|~7.1.0" + "magento/framework": "*", + "magento/module-backend": "*", + "magento/module-catalog": "*", + "magento/module-catalog-search": "*", + "magento/module-config": "*", + "magento/module-customer": "*", + "magento/module-search": "*", + "magento/module-store": "*", + "php": "~7.1.3||~7.2.0" }, "type": "magento2-module", - "version": "100.3.0-dev", "license": [ "proprietary" ], diff --git a/app/code/Magento/Amqp/Setup/ConfigOptionsList.php b/app/code/Magento/Amqp/Setup/ConfigOptionsList.php index e4126f148d651..7b857dc2bcc2d 100644 --- a/app/code/Magento/Amqp/Setup/ConfigOptionsList.php +++ b/app/code/Magento/Amqp/Setup/ConfigOptionsList.php @@ -25,6 +25,7 @@ class ConfigOptionsList implements ConfigOptionsListInterface const INPUT_KEY_QUEUE_AMQP_PASSWORD = 'amqp-password'; const INPUT_KEY_QUEUE_AMQP_VIRTUAL_HOST = 'amqp-virtualhost'; const INPUT_KEY_QUEUE_AMQP_SSL = 'amqp-ssl'; + const INPUT_KEY_QUEUE_AMQP_SSL_OPTIONS = 'amqp-ssl-options'; /** * Path to the values in the deployment config @@ -35,6 +36,7 @@ class ConfigOptionsList implements ConfigOptionsListInterface const CONFIG_PATH_QUEUE_AMQP_PASSWORD = 'queue/amqp/password'; const CONFIG_PATH_QUEUE_AMQP_VIRTUAL_HOST = 'queue/amqp/virtualhost'; const CONFIG_PATH_QUEUE_AMQP_SSL = 'queue/amqp/ssl'; + const CONFIG_PATH_QUEUE_AMQP_SSL_OPTIONS = 'queue/amqp/ssl_options'; /** * Default values @@ -109,6 +111,13 @@ public function getOptions() 'Amqp SSL', self::DEFAULT_AMQP_SSL ), + new TextConfigOption( + self::INPUT_KEY_QUEUE_AMQP_SSL_OPTIONS, + TextConfigOption::FRONTEND_WIZARD_TEXTAREA, + self::CONFIG_PATH_QUEUE_AMQP_SSL_OPTIONS, + 'Amqp SSL Options (JSON)', + self::DEFAULT_AMQP_SSL + ), ]; } @@ -140,6 +149,21 @@ public function createConfig(array $data, DeploymentConfig $deploymentConfig) if (!$this->isDataEmpty($data, self::INPUT_KEY_QUEUE_AMQP_SSL)) { $configData->set(self::CONFIG_PATH_QUEUE_AMQP_SSL, $data[self::INPUT_KEY_QUEUE_AMQP_SSL]); } + if (!$this->isDataEmpty( + $data, + self::INPUT_KEY_QUEUE_AMQP_SSL_OPTIONS + )) { + $options = json_decode( + $data[self::INPUT_KEY_QUEUE_AMQP_SSL_OPTIONS], + true + ); + if ($options !== null) { + $configData->set( + self::CONFIG_PATH_QUEUE_AMQP_SSL_OPTIONS, + $options + ); + } + } } return [$configData]; @@ -154,12 +178,28 @@ public function validate(array $options, DeploymentConfig $deploymentConfig) if (isset($options[self::INPUT_KEY_QUEUE_AMQP_HOST]) && $options[self::INPUT_KEY_QUEUE_AMQP_HOST] !== '') { + if (!$this->isDataEmpty( + $options, + self::INPUT_KEY_QUEUE_AMQP_SSL_OPTIONS + )) { + $sslOptions = json_decode( + $options[self::INPUT_KEY_QUEUE_AMQP_SSL_OPTIONS], + true + ); + } else { + $sslOptions = null; + } + $isSslEnabled = !empty($options[self::INPUT_KEY_QUEUE_AMQP_SSL]) + && $options[self::INPUT_KEY_QUEUE_AMQP_SSL] !== 'false'; + $result = $this->connectionValidator->isConnectionValid( $options[self::INPUT_KEY_QUEUE_AMQP_HOST], $options[self::INPUT_KEY_QUEUE_AMQP_PORT], $options[self::INPUT_KEY_QUEUE_AMQP_USER], $options[self::INPUT_KEY_QUEUE_AMQP_PASSWORD], - $options[self::INPUT_KEY_QUEUE_AMQP_VIRTUAL_HOST] + $options[self::INPUT_KEY_QUEUE_AMQP_VIRTUAL_HOST], + $isSslEnabled, + $sslOptions ); if (!$result) { diff --git a/app/code/Magento/Amqp/Setup/ConnectionValidator.php b/app/code/Magento/Amqp/Setup/ConnectionValidator.php index eb17b3517f0e2..55a11286c7c43 100644 --- a/app/code/Magento/Amqp/Setup/ConnectionValidator.php +++ b/app/code/Magento/Amqp/Setup/ConnectionValidator.php @@ -5,13 +5,27 @@ */ namespace Magento\Amqp\Setup; -use PhpAmqpLib\Connection\AMQPStreamConnection; +use Magento\Framework\Amqp\Connection\Factory as ConnectionFactory; +use Magento\Framework\Amqp\Connection\FactoryOptions; /** * Class ConnectionValidator - validates Amqp related settings */ class ConnectionValidator { + /** + * @var ConnectionFactory + */ + private $connectionFactory; + + /** + * @param ConnectionFactory $connectionFactory + */ + public function __construct(ConnectionFactory $connectionFactory) + { + $this->connectionFactory = $connectionFactory; + } + /** * Checks Amqp Connection * @@ -20,18 +34,33 @@ class ConnectionValidator * @param string $user * @param string $password * @param string $virtualHost + * @param bool $ssl + * @param string[]|null $sslOptions * @return bool true if the connection succeeded, false otherwise */ - public function isConnectionValid($host, $port, $user, $password = '', $virtualHost = '') - { + public function isConnectionValid( + $host, + $port, + $user, + $password = '', + $virtualHost = '', + bool $ssl = false, + array $sslOptions = null + ) { try { - $connection = new AMQPStreamConnection( - $host, - $port, - $user, - $password, - $virtualHost - ); + $options = new FactoryOptions(); + $options->setHost($host); + $options->setPort($port); + $options->setUsername($user); + $options->setPassword($password); + $options->setVirtualHost($virtualHost); + $options->setSslEnabled($ssl); + + if ($sslOptions) { + $options->setSslOptions($sslOptions); + } + + $connection = $this->connectionFactory->create($options); $connection->close(); } catch (\Exception $e) { diff --git a/app/code/Magento/Amqp/Test/Unit/Setup/ConfigOptionsListTest.php b/app/code/Magento/Amqp/Test/Unit/Setup/ConfigOptionsListTest.php index 974be6dbff492..8db9ae73034a2 100644 --- a/app/code/Magento/Amqp/Test/Unit/Setup/ConfigOptionsListTest.php +++ b/app/code/Magento/Amqp/Test/Unit/Setup/ConfigOptionsListTest.php @@ -47,7 +47,7 @@ protected function setUp() ConfigOptionsList::INPUT_KEY_QUEUE_AMQP_PASSWORD => 'password', ConfigOptionsList::INPUT_KEY_QUEUE_AMQP_VIRTUAL_HOST => 'virtual host', ConfigOptionsList::INPUT_KEY_QUEUE_AMQP_SSL => 'ssl', - + ConfigOptionsList::INPUT_KEY_QUEUE_AMQP_SSL_OPTIONS => '{"ssl_option":"test"}', ]; $this->objectManager = new ObjectManager($this); @@ -113,7 +113,14 @@ public function testGetOptions() ConfigOptionsList::CONFIG_PATH_QUEUE_AMQP_SSL, 'Amqp SSL', ConfigOptionsList::DEFAULT_AMQP_SSL - ) + ), + new TextConfigOption( + ConfigOptionsList::INPUT_KEY_QUEUE_AMQP_SSL_OPTIONS, + TextConfigOption::FRONTEND_WIZARD_TEXTAREA, + ConfigOptionsList::CONFIG_PATH_QUEUE_AMQP_SSL_OPTIONS, + 'Amqp SSL Options (JSON)', + ConfigOptionsList::DEFAULT_AMQP_SSL + ), ]; $this->assertEquals($expectedOptions, $this->model->getOptions()); } @@ -167,6 +174,7 @@ public function getCreateConfigDataProvider() ConfigOptionsList::INPUT_KEY_QUEUE_AMQP_PASSWORD => 'password', ConfigOptionsList::INPUT_KEY_QUEUE_AMQP_VIRTUAL_HOST => 'virtual host', ConfigOptionsList::INPUT_KEY_QUEUE_AMQP_SSL => 'ssl', + ConfigOptionsList::INPUT_KEY_QUEUE_AMQP_SSL_OPTIONS => '{"ssl_option":"test"}', ], ['queue' => ['amqp' => @@ -177,6 +185,7 @@ public function getCreateConfigDataProvider() 'password' => 'password', 'virtualhost' => 'virtual host', 'ssl' => 'ssl', + 'ssl_options' => ['ssl_option' => 'test'], ] ] ], @@ -189,6 +198,7 @@ public function getCreateConfigDataProvider() ConfigOptionsList::INPUT_KEY_QUEUE_AMQP_PASSWORD => 'password', ConfigOptionsList::INPUT_KEY_QUEUE_AMQP_VIRTUAL_HOST => 'virtual host', ConfigOptionsList::INPUT_KEY_QUEUE_AMQP_SSL => 'ssl', + ConfigOptionsList::INPUT_KEY_QUEUE_AMQP_SSL_OPTIONS => '{"ssl_option":"test"}', ], ['queue' => ['amqp' => @@ -199,6 +209,7 @@ public function getCreateConfigDataProvider() 'password' => 'password', 'virtualhost' => 'virtual host', 'ssl' => 'ssl', + 'ssl_options' => ['ssl_option' => 'test'], ] ] ], diff --git a/app/code/Magento/Amqp/composer.json b/app/code/Magento/Amqp/composer.json index 255fa5d55d935..23130dfb01a4e 100644 --- a/app/code/Magento/Amqp/composer.json +++ b/app/code/Magento/Amqp/composer.json @@ -5,13 +5,12 @@ "sort-packages": true }, "require": { - "magento/framework": "100.3.*", - "magento/framework-amqp": "100.1.*", - "magento/framework-message-queue": "100.3.*", - "php": "7.0.2|7.0.4|~7.0.6|~7.1.0" + "magento/framework": "*", + "magento/framework-amqp": "*", + "magento/framework-message-queue": "*", + "php": "~7.1.3||~7.2.0" }, "type": "magento2-module", - "version": "100.3.0-dev", "license": [ "proprietary" ], diff --git a/app/code/Magento/Analytics/Model/Config/Backend/Enabled.php b/app/code/Magento/Analytics/Model/Config/Backend/Enabled.php index ac97f2a843e61..a5d885c80c3fc 100644 --- a/app/code/Magento/Analytics/Model/Config/Backend/Enabled.php +++ b/app/code/Magento/Analytics/Model/Config/Backend/Enabled.php @@ -67,12 +67,7 @@ public function afterSave() try { if ($this->isValueChanged()) { $enabled = $this->getData('value'); - - if ($enabled) { - $this->subscriptionHandler->processEnabled(); - } else { - $this->subscriptionHandler->processDisabled(); - } + $enabled ? $this->subscriptionHandler->processEnabled() : $this->subscriptionHandler->processDisabled(); } } catch (\Exception $e) { $this->_logger->error($e->getMessage()); diff --git a/app/code/Magento/Analytics/composer.json b/app/code/Magento/Analytics/composer.json index bdea53c445a34..88127f3c62a92 100644 --- a/app/code/Magento/Analytics/composer.json +++ b/app/code/Magento/Analytics/composer.json @@ -2,15 +2,14 @@ "name": "magento/module-analytics", "description": "N/A", "require": { - "php": "7.0.2|7.0.4|~7.0.6|~7.1.0", - "magento/module-backend": "100.3.*", - "magento/module-config": "100.3.*", - "magento/module-integration": "100.3.*", - "magento/module-store": "100.3.*", - "magento/framework": "100.3.*" + "php": "~7.1.3||~7.2.0", + "magento/module-backend": "*", + "magento/module-config": "*", + "magento/module-integration": "*", + "magento/module-store": "*", + "magento/framework": "*" }, "type": "magento2-module", - "version": "100.3.0-dev", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Analytics/etc/adminhtml/system.xml b/app/code/Magento/Analytics/etc/adminhtml/system.xml index 889517e629e04..4e21648d00ce8 100644 --- a/app/code/Magento/Analytics/etc/adminhtml/system.xml +++ b/app/code/Magento/Analytics/etc/adminhtml/system.xml @@ -17,14 +17,14 @@ Your reports can be accessed securely on a personalized dashboard outside of the admin panel by clicking on the "Go to Advanced Reporting" link.
For more information, see our terms and conditions.]]> - + Magento\Config\Model\Config\Source\Enabledisable Magento\Analytics\Model\Config\Backend\Enabled Magento\Analytics\Block\Adminhtml\System\Config\SubscriptionStatusLabel analytics/subscription/enabled - + Magento\Analytics\Block\Adminhtml\System\Config\CollectionTimeLabel Magento\Analytics\Model\Config\Backend\CollectionTime diff --git a/app/code/Magento/AsynchronousOperations/Api/BulkStatusInterface.php b/app/code/Magento/AsynchronousOperations/Api/BulkStatusInterface.php new file mode 100644 index 0000000000000..88db2d6d80141 --- /dev/null +++ b/app/code/Magento/AsynchronousOperations/Api/BulkStatusInterface.php @@ -0,0 +1,36 @@ +getData(self::BULK_UUID); + } + + /** + * @inheritDoc + */ + public function setBulkUuid($bulkUuid) + { + return $this->setData(self::BULK_UUID, $bulkUuid); + } + + /** + * @inheritDoc + */ + public function getRequestItems() + { + return $this->getData(self::REQUEST_ITEMS); + } + + /** + * @inheritDoc + */ + public function setRequestItems($requestItems) + { + return $this->setData(self::REQUEST_ITEMS, $requestItems); + } + + /** + * @inheritdoc + */ + public function setErrors($isErrors = false) + { + return $this->setData(self::ERRORS, $isErrors); + } + + /** + * @inheritdoc + */ + public function isErrors() + { + return $this->getData(self::ERRORS); + } + + /** + * @inheritDoc + */ + public function getExtensionAttributes() + { + return $this->getData(self::EXTENSION_ATTRIBUTES_KEY); + } + + /** + * @inheritDoc + */ + public function setExtensionAttributes( + \Magento\AsynchronousOperations\Api\Data\AsyncResponseExtensionInterface $extensionAttributes + ) { + return $this->setData(self::EXTENSION_ATTRIBUTES_KEY, $extensionAttributes); + } +} diff --git a/app/code/Magento/AsynchronousOperations/Model/BulkOperationsStatus.php b/app/code/Magento/AsynchronousOperations/Model/BulkOperationsStatus.php new file mode 100644 index 0000000000000..5fc164ec833d8 --- /dev/null +++ b/app/code/Magento/AsynchronousOperations/Model/BulkOperationsStatus.php @@ -0,0 +1,150 @@ +operationCollectionFactory = $operationCollection; + $this->bulkStatus = $bulkStatus; + $this->bulkDetailedFactory = $bulkDetailedFactory; + $this->bulkShortFactory = $bulkShortFactory; + $this->entityManager = $entityManager; + } + + /** + * @inheritDoc + */ + public function getFailedOperationsByBulkId($bulkUuid, $failureType = null) + { + return $this->bulkStatus->getFailedOperationsByBulkId($bulkUuid, $failureType); + } + + /** + * @inheritDoc + */ + public function getOperationsCountByBulkIdAndStatus($bulkUuid, $status) + { + return $this->bulkStatus->getOperationsCountByBulkIdAndStatus($bulkUuid, $status); + } + + /** + * @inheritDoc + */ + public function getBulksByUser($userId) + { + return $this->bulkStatus->getBulksByUser($userId); + } + + /** + * @inheritDoc + */ + public function getBulkStatus($bulkUuid) + { + return $this->bulkStatus->getBulkStatus($bulkUuid); + } + + /** + * @inheritDoc + */ + public function getBulkDetailedStatus($bulkUuid) + { + $bulkSummary = $this->bulkDetailedFactory->create(); + + /** @var \Magento\AsynchronousOperations\Api\Data\DetailedBulkOperationsStatusInterface $bulk */ + $bulk = $this->entityManager->load($bulkSummary, $bulkUuid); + + if ($bulk->getBulkId() === null) { + throw new NoSuchEntityException( + __( + 'Bulk uuid %bulkUuid not exist', + ['bulkUuid' => $bulkUuid] + ) + ); + } + $operations = $this->operationCollectionFactory->create()->addFieldToFilter('bulk_uuid', $bulkUuid)->getItems(); + $bulk->setOperationsList($operations); + + return $bulk; + } + + /** + * @inheritDoc + */ + public function getBulkShortStatus($bulkUuid) + { + $bulkSummary = $this->bulkShortFactory->create(); + + /** @var \Magento\AsynchronousOperations\Api\Data\BulkOperationsStatusInterface $bulk */ + $bulk = $this->entityManager->load($bulkSummary, $bulkUuid); + if ($bulk->getBulkId() === null) { + throw new NoSuchEntityException( + __( + 'Bulk uuid %bulkUuid not exist', + ['bulkUuid' => $bulkUuid] + ) + ); + } + $operations = $this->operationCollectionFactory->create()->addFieldToFilter('bulk_uuid', $bulkUuid)->getItems(); + $bulk->setOperationsList($operations); + + return $bulk; + } +} diff --git a/app/code/Magento/AsynchronousOperations/Model/BulkStatus/CalculatedStatusSql.php b/app/code/Magento/AsynchronousOperations/Model/BulkStatus/CalculatedStatusSql.php index dda3ed3b6737a..7bdf8a5b7d400 100644 --- a/app/code/Magento/AsynchronousOperations/Model/BulkStatus/CalculatedStatusSql.php +++ b/app/code/Magento/AsynchronousOperations/Model/BulkStatus/CalculatedStatusSql.php @@ -6,7 +6,6 @@ namespace Magento\AsynchronousOperations\Model\BulkStatus; -use Magento\AsynchronousOperations\Api\Data\OperationInterface; use Magento\AsynchronousOperations\Api\Data\BulkSummaryInterface; class CalculatedStatusSql diff --git a/app/code/Magento/AsynchronousOperations/Model/BulkStatus/Detailed.php b/app/code/Magento/AsynchronousOperations/Model/BulkStatus/Detailed.php new file mode 100644 index 0000000000000..334abb33fd106 --- /dev/null +++ b/app/code/Magento/AsynchronousOperations/Model/BulkStatus/Detailed.php @@ -0,0 +1,31 @@ +getData(self::OPERATIONS_LIST); + } + + /** + * @inheritDoc + */ + public function setOperationsList($operationStatusList) + { + return $this->setData(self::OPERATIONS_LIST, $operationStatusList); + } +} diff --git a/app/code/Magento/AsynchronousOperations/Model/BulkStatus/Short.php b/app/code/Magento/AsynchronousOperations/Model/BulkStatus/Short.php new file mode 100644 index 0000000000000..c6aa99e67202b --- /dev/null +++ b/app/code/Magento/AsynchronousOperations/Model/BulkStatus/Short.php @@ -0,0 +1,31 @@ +getData(self::OPERATIONS_LIST); + } + + /** + * @inheritDoc + */ + public function setOperationsList($operationStatusList) + { + return $this->setData(self::OPERATIONS_LIST, $operationStatusList); + } +} diff --git a/app/code/Magento/AsynchronousOperations/Model/ConfigInterface.php b/app/code/Magento/AsynchronousOperations/Model/ConfigInterface.php new file mode 100644 index 0000000000000..de0f89a71650a --- /dev/null +++ b/app/code/Magento/AsynchronousOperations/Model/ConfigInterface.php @@ -0,0 +1,60 @@ + self::SYSTEM_TOPIC_NAME, + CommunicationConfig::TOPIC_IS_SYNCHRONOUS => false, + CommunicationConfig::TOPIC_REQUEST => OperationInterface::class, + CommunicationConfig::TOPIC_REQUEST_TYPE => CommunicationConfig::TOPIC_REQUEST_TYPE_CLASS, + CommunicationConfig::TOPIC_RESPONSE => null, + CommunicationConfig::TOPIC_HANDLERS => [], + ]; + /**#@-*/ + + /** + * Get array of generated topics name and related to this topic service class and methods + * + * @return array + */ + public function getServices(); + + /** + * Get topic name 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 getTopicName($routeUrl, $httpMethod); +} diff --git a/app/code/Magento/AsynchronousOperations/Model/Entity/BulkSummaryMapper.php b/app/code/Magento/AsynchronousOperations/Model/Entity/BulkSummaryMapper.php index 94ebbdca445c4..4abbde4c3602b 100644 --- a/app/code/Magento/AsynchronousOperations/Model/Entity/BulkSummaryMapper.php +++ b/app/code/Magento/AsynchronousOperations/Model/Entity/BulkSummaryMapper.php @@ -8,7 +8,6 @@ use Magento\Framework\EntityManager\MapperInterface; use Magento\Framework\App\ResourceConnection; use Magento\Framework\EntityManager\MetadataPool; -use Magento\AsynchronousOperations\Api\Data\BulkSummaryInterface; /** * @deprecated 100.2.0 diff --git a/app/code/Magento/AsynchronousOperations/Model/ItemStatus.php b/app/code/Magento/AsynchronousOperations/Model/ItemStatus.php new file mode 100644 index 0000000000000..b493e0bb663d3 --- /dev/null +++ b/app/code/Magento/AsynchronousOperations/Model/ItemStatus.php @@ -0,0 +1,103 @@ +getData(self::ENTITY_ID); + } + + /** + * @inheritDoc + */ + public function setId($entityId) + { + return $this->setData(self::ENTITY_ID, $entityId); + } + + /** + * @inheritDoc + */ + public function getDataHash() + { + return $this->getData(self::DATA_HASH); + } + + /** + * @inheritDoc + */ + public function setDataHash($hash) + { + return $this->setData(self::DATA_HASH, $hash); + } + + /** + * @inheritDoc + */ + public function getStatus() + { + return $this->getData(self::STATUS); + } + + /** + * @inheritDoc + */ + public function setStatus($status = self::STATUS_ACCEPTED) + { + return $this->setData(self::STATUS, $status); + } + + /** + * @inheritDoc + */ + public function getErrorMessage() + { + return $this->getData(self::ERROR_MESSAGE); + } + + /** + * @inheritDoc + */ + public function setErrorMessage($errorMessage = null) + { + if ($errorMessage instanceof \Exception) { + $errorMessage = $errorMessage->getMessage(); + } + + return $this->setData(self::ERROR_MESSAGE, $errorMessage); + } + + /** + * @inheritDoc + */ + public function getErrorCode() + { + return $this->getData(self::ERROR_CODE); + } + + /** + * @inheritDoc + */ + public function setErrorCode($errorCode = null) + { + if ($errorCode instanceof \Exception) { + $errorCode = $errorCode->getCode(); + } + + return $this->setData(self::ERROR_CODE, (int) $errorCode); + } +} diff --git a/app/code/Magento/AsynchronousOperations/Model/MassConsumer.php b/app/code/Magento/AsynchronousOperations/Model/MassConsumer.php new file mode 100644 index 0000000000000..28bc8141a8e99 --- /dev/null +++ b/app/code/Magento/AsynchronousOperations/Model/MassConsumer.php @@ -0,0 +1,145 @@ +invoker = $invoker; + $this->resource = $resource; + $this->messageController = $messageController; + $this->configuration = $configuration; + $this->operationProcessor = $operationProcessorFactory->create([ + 'configuration' => $configuration + ]); + $this->logger = $logger; + } + + /** + * {@inheritdoc} + */ + public function process($maxNumberOfMessages = null) + { + $queue = $this->configuration->getQueue(); + + if (!isset($maxNumberOfMessages)) { + $queue->subscribe($this->getTransactionCallback($queue)); + } else { + $this->invoker->invoke($queue, $maxNumberOfMessages, $this->getTransactionCallback($queue)); + } + } + + /** + * Get transaction callback. This handles the case of async. + * + * @param QueueInterface $queue + * @return \Closure + */ + private function getTransactionCallback(QueueInterface $queue) + { + return function (EnvelopeInterface $message) use ($queue) { + /** @var LockInterface $lock */ + $lock = null; + try { + $topicName = $message->getProperties()['topic_name']; + $lock = $this->messageController->lock($message, $this->configuration->getConsumerName()); + + $allowedTopics = $this->configuration->getTopicNames(); + if (in_array($topicName, $allowedTopics)) { + $this->operationProcessor->process($message->getBody()); + } else { + $queue->reject($message); + return; + } + $queue->acknowledge($message); + } catch (MessageLockException $exception) { + $queue->acknowledge($message); + } catch (ConnectionLostException $e) { + if ($lock) { + $this->resource->getConnection() + ->delete($this->resource->getTableName('queue_lock'), ['id = ?' => $lock->getId()]); + } + } catch (NotFoundException $e) { + $queue->acknowledge($message); + $this->logger->warning($e->getMessage()); + } catch (\Exception $e) { + $queue->reject($message, false, $e->getMessage()); + if ($lock) { + $this->resource->getConnection() + ->delete($this->resource->getTableName('queue_lock'), ['id = ?' => $lock->getId()]); + } + } + }; + } +} diff --git a/app/code/Magento/AsynchronousOperations/Model/MassPublisher.php b/app/code/Magento/AsynchronousOperations/Model/MassPublisher.php new file mode 100644 index 0000000000000..5f0f8e28f9fe6 --- /dev/null +++ b/app/code/Magento/AsynchronousOperations/Model/MassPublisher.php @@ -0,0 +1,106 @@ +exchangeRepository = $exchangeRepository; + $this->envelopeFactory = $envelopeFactory; + $this->messageEncoder = $messageEncoder; + $this->messageValidator = $messageValidator; + $this->publisherConfig = $publisherConfig; + $this->messageIdGenerator = $messageIdGenerator; + } + + /** + * {@inheritdoc} + */ + public function publish($topicName, $data) + { + $envelopes = []; + foreach ($data as $message) { + $this->messageValidator->validate(AsyncConfig::SYSTEM_TOPIC_NAME, $message); + $message = $this->messageEncoder->encode(AsyncConfig::SYSTEM_TOPIC_NAME, $message); + $envelopes[] = $this->envelopeFactory->create( + [ + 'body' => $message, + 'properties' => [ + 'delivery_mode' => 2, + 'message_id' => $this->messageIdGenerator->generate($topicName), + ] + ] + ); + } + $publisher = $this->publisherConfig->getPublisher($topicName); + $connectionName = $publisher->getConnection()->getName(); + $exchange = $this->exchangeRepository->getByConnectionName($connectionName); + $exchange->enqueue($topicName, $envelopes); + return null; + } +} diff --git a/app/code/Magento/AsynchronousOperations/Model/MassSchedule.php b/app/code/Magento/AsynchronousOperations/Model/MassSchedule.php new file mode 100644 index 0000000000000..2d516e82f4016 --- /dev/null +++ b/app/code/Magento/AsynchronousOperations/Model/MassSchedule.php @@ -0,0 +1,155 @@ +identityService = $identityService; + $this->itemStatusInterfaceFactory = $itemStatusInterfaceFactory; + $this->asyncResponseFactory = $asyncResponseFactory; + $this->bulkManagement = $bulkManagement; + $this->logger = $logger; + $this->operationRepository = $operationRepository; + } + + /** + * Schedule new bulk operation based on the list of entities + * + * @param $topicName + * @param $entitiesArray + * @param null $groupId + * @param null $userId + * @return AsyncResponseInterface + * @throws BulkException + * @throws LocalizedException + */ + public function publishMass($topicName, array $entitiesArray, $groupId = null, $userId = null) + { + $bulkDescription = __('Topic %1', $topicName); + + if ($groupId == null) { + $groupId = $this->identityService->generateId(); + + /** create new bulk without operations */ + if (!$this->bulkManagement->scheduleBulk($groupId, [], $bulkDescription, $userId)) { + throw new LocalizedException( + __('Something went wrong while processing the request.') + ); + } + } + + $operations = []; + $requestItems = []; + $bulkException = new BulkException(); + foreach ($entitiesArray as $key => $entityParams) { + /** @var \Magento\AsynchronousOperations\Api\Data\ItemStatusInterface $requestItem */ + $requestItem = $this->itemStatusInterfaceFactory->create(); + + try { + $operations[] = $this->operationRepository->createByTopic($topicName, $entityParams, $groupId); + $requestItem->setId($key); + $requestItem->setStatus(ItemStatusInterface::STATUS_ACCEPTED); + $requestItems[] = $requestItem; + } catch (\Exception $exception) { + $this->logger->error($exception); + $requestItem->setId($key); + $requestItem->setStatus(ItemStatusInterface::STATUS_REJECTED); + $requestItem->setErrorMessage($exception); + $requestItem->setErrorCode($exception); + $requestItems[] = $requestItem; + $bulkException->addException(new LocalizedException( + __('Error processing %key element of input data', ['key' => $key]), + $exception + )); + } + } + + if (!$this->bulkManagement->scheduleBulk($groupId, $operations, $bulkDescription, $userId)) { + throw new LocalizedException( + __('Something went wrong while processing the request.') + ); + } + /** @var AsyncResponseInterface $asyncResponse */ + $asyncResponse = $this->asyncResponseFactory->create(); + $asyncResponse->setBulkUuid($groupId); + $asyncResponse->setRequestItems($requestItems); + + if ($bulkException->wasErrorAdded()) { + $asyncResponse->setErrors(true); + $bulkException->addData($asyncResponse); + throw $bulkException; + } else { + $asyncResponse->setErrors(false); + } + + return $asyncResponse; + } +} diff --git a/app/code/Magento/AsynchronousOperations/Model/Operation.php b/app/code/Magento/AsynchronousOperations/Model/Operation.php index dbfce3ccd8b1c..70cc9f0ebc575 100644 --- a/app/code/Magento/AsynchronousOperations/Model/Operation.php +++ b/app/code/Magento/AsynchronousOperations/Model/Operation.php @@ -5,14 +5,14 @@ */ namespace Magento\AsynchronousOperations\Model; -use Magento\AsynchronousOperations\Api\Data\OperationInterface; +use Magento\AsynchronousOperations\Api\Data\DetailedOperationStatusInterface; use Magento\Framework\DataObject; use Magento\Framework\Api\ExtensibleDataInterface; /** * Class Operation */ -class Operation extends DataObject implements OperationInterface, ExtensibleDataInterface +class Operation extends DataObject implements DetailedOperationStatusInterface, ExtensibleDataInterface { /** * @inheritDoc @@ -78,6 +78,22 @@ public function setSerializedData($serializedData) return $this->setData(self::SERIALIZED_DATA, $serializedData); } + /** + * @inheritDoc + */ + public function getResultSerializedData() + { + return $this->getData(self::RESULT_SERIALIZED_DATA); + } + + /** + * @inheritDoc + */ + public function setResultSerializedData($resultSerializedData) + { + return $this->setData(self::RESULT_SERIALIZED_DATA, $resultSerializedData); + } + /** * @inheritDoc */ diff --git a/app/code/Magento/AsynchronousOperations/Model/Operation/Details.php b/app/code/Magento/AsynchronousOperations/Model/Operation/Details.php index 398934f093350..d248f9c3e9276 100644 --- a/app/code/Magento/AsynchronousOperations/Model/Operation/Details.php +++ b/app/code/Magento/AsynchronousOperations/Model/Operation/Details.php @@ -3,9 +3,11 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\AsynchronousOperations\Model\Operation; use Magento\Framework\Bulk\OperationInterface; +use Magento\Framework\Bulk\BulkStatusInterface; class Details { @@ -19,23 +21,36 @@ class Details */ private $bulkStatus; + /** + * @var null + */ + private $bulkUuid; + /** * Map between status codes and human readable indexes + * * @var array */ private $statusMap = [ - OperationInterface::STATUS_TYPE_COMPLETE => 'operations_successful', - OperationInterface::STATUS_TYPE_RETRIABLY_FAILED => 'failed_retriable', + OperationInterface::STATUS_TYPE_COMPLETE => 'operations_successful', + OperationInterface::STATUS_TYPE_RETRIABLY_FAILED => 'failed_retriable', OperationInterface::STATUS_TYPE_NOT_RETRIABLY_FAILED => 'failed_not_retriable', + OperationInterface::STATUS_TYPE_OPEN => 'open', + OperationInterface::STATUS_TYPE_REJECTED => 'rejected', ]; /** + * Init dependencies. + * * @param \Magento\Framework\Bulk\BulkStatusInterface $bulkStatus + * @param null $bulkUuid */ public function __construct( - \Magento\Framework\Bulk\BulkStatusInterface $bulkStatus + BulkStatusInterface $bulkStatus, + $bulkUuid = null ) { $this->bulkStatus = $bulkStatus; + $this->bulkUuid = $bulkUuid; } /** @@ -47,11 +62,12 @@ public function __construct( public function getDetails($bulkUuid) { $details = [ - 'operations_total' => 0, + 'operations_total' => 0, 'operations_successful' => 0, - 'operations_failed' => 0, - 'failed_retriable' => 0, - 'failed_not_retriable' => 0, + 'operations_failed' => 0, + 'failed_retriable' => 0, + 'failed_not_retriable' => 0, + 'rejected' => 0, ]; if (array_key_exists($bulkUuid, $this->operationCache)) { @@ -65,13 +81,84 @@ public function getDetails($bulkUuid) ); } - // total is sum of successful, retriable, not retriable and open operations - $details['operations_total'] = array_sum($details) + $this->bulkStatus->getOperationsCountByBulkIdAndStatus( - $bulkUuid, - OperationInterface::STATUS_TYPE_OPEN - ); + $details['operations_total'] = array_sum($details); $details['operations_failed'] = $details['failed_retriable'] + $details['failed_not_retriable']; $this->operationCache[$bulkUuid] = $details; + return $details; } + + /** + * @inheritDoc + */ + public function getOperationsTotal() + { + $this->getDetails($this->bulkUuid); + + return $this->operationCache[$this->bulkUuid]['operations_total']; + } + + /** + * @inheritDoc + */ + public function getOpen() + { + $this->getDetails($this->bulkUuid); + $statusKey = $this->statusMap[OperationInterface::STATUS_TYPE_OPEN]; + + return $this->operationCache[$this->bulkUuid][$statusKey]; + } + + /** + * @inheritDoc + */ + public function getOperationsSuccessful() + { + $this->getDetails($this->bulkUuid); + $statusKey = $this->statusMap[OperationInterface::STATUS_TYPE_COMPLETE]; + + return $this->operationCache[$this->bulkUuid][$statusKey]; + } + + /** + * @inheritDoc + */ + public function getTotalFailed() + { + $this->getDetails($this->bulkUuid); + + return $this->operationCache[$this->bulkUuid]['operations_failed']; + } + + /** + * @inheritDoc + */ + public function getFailedNotRetriable() + { + $statusKey = $this->statusMap[OperationInterface::STATUS_TYPE_NOT_RETRIABLY_FAILED]; + + return $this->operationCache[$this->bulkUuid][$statusKey]; + } + + /** + * @inheritDoc + */ + public function getFailedRetriable() + { + $this->getDetails($this->bulkUuid); + $statusKey = $this->statusMap[OperationInterface::STATUS_TYPE_RETRIABLY_FAILED]; + + return $this->operationCache[$this->bulkUuid][$statusKey]; + } + + /** + * @inheritDoc + */ + public function getRejected() + { + $this->getDetails($this->bulkUuid); + $statusKey = $this->statusMap[OperationInterface::STATUS_TYPE_REJECTED]; + + return $this->operationCache[$this->bulkUuid][$statusKey]; + } } diff --git a/app/code/Magento/AsynchronousOperations/Model/OperationManagement.php b/app/code/Magento/AsynchronousOperations/Model/OperationManagement.php index 54b8a76758435..ce780a4ba858d 100644 --- a/app/code/Magento/AsynchronousOperations/Model/OperationManagement.php +++ b/app/code/Magento/AsynchronousOperations/Model/OperationManagement.php @@ -6,7 +6,7 @@ namespace Magento\AsynchronousOperations\Model; -use Magento\AsynchronousOperations\Api\Data\OperationInterfaceFactory; +use Magento\AsynchronousOperations\Api\Data\DetailedOperationStatusInterfaceFactory; use Magento\Framework\EntityManager\EntityManager; /** @@ -20,7 +20,7 @@ class OperationManagement implements \Magento\Framework\Bulk\OperationManagement private $entityManager; /** - * @var OperationInterfaceFactory + * @var DetailedOperationStatusInterfaceFactory */ private $operationFactory; @@ -33,24 +33,30 @@ class OperationManagement implements \Magento\Framework\Bulk\OperationManagement * OperationManagement constructor. * * @param EntityManager $entityManager - * @param OperationInterfaceFactory $operationFactory + * @param DetailedOperationStatusInterfaceFactory $operationFactory * @param \Psr\Log\LoggerInterface $logger */ public function __construct( EntityManager $entityManager, - OperationInterfaceFactory $operationFactory, + DetailedOperationStatusInterfaceFactory $operationFactory, \Psr\Log\LoggerInterface $logger ) { $this->entityManager = $entityManager; - $this->operationFactory= $operationFactory; + $this->operationFactory = $operationFactory; $this->logger = $logger; } /** * @inheritDoc */ - public function changeOperationStatus($operationId, $status, $errorCode = null, $message = null, $data = null) - { + public function changeOperationStatus( + $operationId, + $status, + $errorCode = null, + $message = null, + $data = null, + $resultData = null + ) { try { $operationEntity = $this->operationFactory->create(); $this->entityManager->load($operationEntity, $operationId); @@ -58,6 +64,8 @@ public function changeOperationStatus($operationId, $status, $errorCode = null, $operationEntity->setStatus($status); $operationEntity->setResultMessage($message); $operationEntity->setSerializedData($data); + $operationEntity->setResultSerializedData($resultData); + $operationEntity->setResultSerializedData($resultData); $this->entityManager->save($operationEntity); } catch (\Exception $exception) { $this->logger->critical($exception->getMessage()); diff --git a/app/code/Magento/AsynchronousOperations/Model/OperationProcessor.php b/app/code/Magento/AsynchronousOperations/Model/OperationProcessor.php new file mode 100644 index 0000000000000..6826c34fd35f0 --- /dev/null +++ b/app/code/Magento/AsynchronousOperations/Model/OperationProcessor.php @@ -0,0 +1,227 @@ +messageValidator = $messageValidator; + $this->messageEncoder = $messageEncoder; + $this->configuration = $configuration; + $this->jsonHelper = $jsonHelper; + $this->operationManagement = $operationManagement; + $this->logger = $logger; + $this->serviceOutputProcessor = $serviceOutputProcessor; + $this->communicationConfig = $communicationConfig; + } + + /** + * Process topic-based encoded message + * + * @param string $encodedMessage + * @return void + */ + public function process(string $encodedMessage) + { + $operation = $this->messageEncoder->decode(AsyncConfig::SYSTEM_TOPIC_NAME, $encodedMessage); + $this->messageValidator->validate(AsyncConfig::SYSTEM_TOPIC_NAME, $operation); + + $status = OperationInterface::STATUS_TYPE_COMPLETE; + $errorCode = null; + $messages = []; + $topicName = $operation->getTopicName(); + $handlers = $this->configuration->getHandlers($topicName); + try { + $data = $this->jsonHelper->unserialize($operation->getSerializedData()); + $entityParams = $this->messageEncoder->decode($topicName, $data['meta_information']); + $this->messageValidator->validate($topicName, $entityParams); + } catch (\Exception $e) { + $this->logger->error($e->getMessage()); + $status = OperationInterface::STATUS_TYPE_NOT_RETRIABLY_FAILED; + $errorCode = $e->getCode(); + $messages[] = $e->getMessage(); + } + + $outputData = null; + if ($errorCode === null) { + foreach ($handlers as $callback) { + $result = $this->executeHandler($callback, $entityParams); + $status = $result['status']; + $errorCode = $result['error_code']; + $messages = array_merge($messages, $result['messages']); + $outputData = $result['output_data']; + } + } + + if (isset($outputData)) { + try { + $communicationConfig = $this->communicationConfig->getTopic($topicName); + $asyncHandler = + $communicationConfig[CommunicationConfig::TOPIC_HANDLERS][AsyncConfig::DEFAULT_HANDLER_NAME]; + $serviceClass = $asyncHandler[CommunicationConfig::HANDLER_TYPE]; + $serviceMethod = $asyncHandler[CommunicationConfig::HANDLER_METHOD]; + $outputData = $this->serviceOutputProcessor->process( + $outputData, + $serviceClass, + $serviceMethod + ); + $outputData = $this->jsonHelper->serialize($outputData); + } catch (\Exception $e) { + $messages[] = $e->getMessage(); + } + } + + $serializedData = (isset($errorCode)) ? $operation->getSerializedData() : null; + $this->operationManagement->changeOperationStatus( + $operation->getId(), + $status, + $errorCode, + implode('; ', $messages), + $serializedData, + $outputData + ); + } + + /** + * Execute topic handler + * + * @param $callback + * @param $entityParams + * @return array + */ + private function executeHandler($callback, $entityParams) + { + $result = [ + 'status' => OperationInterface::STATUS_TYPE_COMPLETE, + 'error_code' => null, + 'messages' => [], + 'output_data' => null + ]; + try { + $result['output_data'] = call_user_func_array($callback, $entityParams); + $result['messages'][] = sprintf('Service execution success %s::%s', get_class($callback[0]), $callback[1]); + } catch (\Zend_Db_Adapter_Exception $e) { + $this->logger->critical($e->getMessage()); + if ($e instanceof LockWaitException + || $e instanceof DeadlockException + || $e instanceof ConnectionException + ) { + $result['status'] = OperationInterface::STATUS_TYPE_RETRIABLY_FAILED; + $result['error_code'] = $e->getCode(); + $result['messages'][] = __($e->getMessage()); + } else { + $result['status'] = OperationInterface::STATUS_TYPE_NOT_RETRIABLY_FAILED; + $result['error_code'] = $e->getCode(); + $result['messages'][] = + __('Sorry, something went wrong during product prices update. Please see log for details.'); + } + } catch (NoSuchEntityException $e) { + $this->logger->error($e->getMessage()); + $result['status'] = ($e instanceof TemporaryStateExceptionInterface) ? + OperationInterface::STATUS_TYPE_NOT_RETRIABLY_FAILED : + OperationInterface::STATUS_TYPE_NOT_RETRIABLY_FAILED; + $result['error_code'] = $e->getCode(); + $result['messages'][] = $e->getMessage(); + } catch (LocalizedException $e) { + $this->logger->error($e->getMessage()); + $result['status'] = OperationInterface::STATUS_TYPE_NOT_RETRIABLY_FAILED; + $result['error_code'] = $e->getCode(); + $result['messages'][] = $e->getMessage(); + } catch (\Exception $e) { + $this->logger->error($e->getMessage()); + $result['status'] = OperationInterface::STATUS_TYPE_NOT_RETRIABLY_FAILED; + $result['error_code'] = $e->getCode(); + $result['messages'][] = $e->getMessage(); + } + return $result; + } +} diff --git a/app/code/Magento/AsynchronousOperations/Model/OperationStatus.php b/app/code/Magento/AsynchronousOperations/Model/OperationStatus.php new file mode 100644 index 0000000000000..5c975bd1a9a45 --- /dev/null +++ b/app/code/Magento/AsynchronousOperations/Model/OperationStatus.php @@ -0,0 +1,52 @@ +getData(OperationInterface::ID); + } + + /** + * @inheritDoc + */ + public function getStatus() + { + return $this->getData(OperationInterface::STATUS); + } + + /** + * @inheritDoc + */ + public function getResultMessage() + { + return $this->getData(OperationInterface::RESULT_MESSAGE); + } + + /** + * @inheritDoc + */ + public function getErrorCode() + { + return $this->getData(OperationInterface::ERROR_CODE); + } +} diff --git a/app/code/Magento/AsynchronousOperations/Model/ResourceModel/Operation/OperationRepository.php b/app/code/Magento/AsynchronousOperations/Model/ResourceModel/Operation/OperationRepository.php new file mode 100644 index 0000000000000..54e65cc3470dd --- /dev/null +++ b/app/code/Magento/AsynchronousOperations/Model/ResourceModel/Operation/OperationRepository.php @@ -0,0 +1,98 @@ +operationFactory = $operationFactory; + $this->jsonSerializer = $jsonSerializer; + $this->messageEncoder = $messageEncoder; + $this->messageValidator = $messageValidator; + $this->entityManager = $entityManager; + } + + /** + * @param $topicName + * @param $entityParams + * @param $groupId + * @return mixed + */ + public function createByTopic($topicName, $entityParams, $groupId) + { + $this->messageValidator->validate($topicName, $entityParams); + $encodedMessage = $this->messageEncoder->encode($topicName, $entityParams); + + $serializedData = [ + 'entity_id' => null, + 'entity_link' => '', + 'meta_information' => $encodedMessage, + ]; + $data = [ + 'data' => [ + OperationInterface::BULK_ID => $groupId, + OperationInterface::TOPIC_NAME => $topicName, + OperationInterface::SERIALIZED_DATA => $this->jsonSerializer->serialize($serializedData), + OperationInterface::STATUS => OperationInterface::STATUS_TYPE_OPEN, + ], + ]; + + /** @var \Magento\AsynchronousOperations\Api\Data\OperationInterface $operation */ + $operation = $this->operationFactory->create($data); + return $this->entityManager->save($operation); + } +} diff --git a/app/code/Magento/AsynchronousOperations/Model/ResourceModel/System/Message/Collection/Synchronized/Plugin.php b/app/code/Magento/AsynchronousOperations/Model/ResourceModel/System/Message/Collection/Synchronized/Plugin.php index 76c7820fa9938..8457a641ed9a9 100644 --- a/app/code/Magento/AsynchronousOperations/Model/ResourceModel/System/Message/Collection/Synchronized/Plugin.php +++ b/app/code/Magento/AsynchronousOperations/Model/ResourceModel/System/Message/Collection/Synchronized/Plugin.php @@ -143,7 +143,7 @@ private function getText($operationDetails) $summaryReport .= __( '%1 item(s) have been successfully updated.', $operationDetails['operations_successful'] - ) ; + ); } if ($operationDetails['operations_failed'] > 0) { diff --git a/app/code/Magento/AsynchronousOperations/Model/StatusMapper.php b/app/code/Magento/AsynchronousOperations/Model/StatusMapper.php index 3ad260cf26a8a..e5aee6d2f59fa 100644 --- a/app/code/Magento/AsynchronousOperations/Model/StatusMapper.php +++ b/app/code/Magento/AsynchronousOperations/Model/StatusMapper.php @@ -25,6 +25,7 @@ public function operationStatusToBulkSummaryStatus($operationStatus) $statusMapping = [ OperationInterface::STATUS_TYPE_NOT_RETRIABLY_FAILED => BulkSummaryInterface::FINISHED_WITH_FAILURE, OperationInterface::STATUS_TYPE_RETRIABLY_FAILED => BulkSummaryInterface::FINISHED_WITH_FAILURE, + OperationInterface::STATUS_TYPE_REJECTED => BulkSummaryInterface::FINISHED_WITH_FAILURE, OperationInterface::STATUS_TYPE_COMPLETE => BulkSummaryInterface::FINISHED_SUCCESSFULLY, OperationInterface::STATUS_TYPE_OPEN => BulkSummaryInterface::IN_PROGRESS, BulkSummaryInterface::NOT_STARTED => BulkSummaryInterface::NOT_STARTED @@ -47,7 +48,8 @@ public function bulkSummaryStatusToOperationStatus($bulkStatus) $statusMapping = [ BulkSummaryInterface::FINISHED_WITH_FAILURE => [ OperationInterface::STATUS_TYPE_NOT_RETRIABLY_FAILED, - OperationInterface::STATUS_TYPE_RETRIABLY_FAILED + OperationInterface::STATUS_TYPE_RETRIABLY_FAILED, + OperationInterface::STATUS_TYPE_REJECTED ], BulkSummaryInterface::FINISHED_SUCCESSFULLY => OperationInterface::STATUS_TYPE_COMPLETE, BulkSummaryInterface::IN_PROGRESS => OperationInterface::STATUS_TYPE_OPEN, diff --git a/app/code/Magento/AsynchronousOperations/Test/Unit/Model/BulkStatusTest.php b/app/code/Magento/AsynchronousOperations/Test/Unit/Model/BulkStatusTest.php index cda55161ab852..7a2f7941f9c04 100644 --- a/app/code/Magento/AsynchronousOperations/Test/Unit/Model/BulkStatusTest.php +++ b/app/code/Magento/AsynchronousOperations/Test/Unit/Model/BulkStatusTest.php @@ -54,11 +54,26 @@ class BulkStatusTest extends \PHPUnit\Framework\TestCase */ private $metadataPoolMock; + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $bulkDetailedFactory; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $bulkShortFactory; + /** * @var \PHPUnit_Framework_MockObject_MockObject */ private $entityMetadataMock; + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $entityManager; + /** * @var \PHPUnit_Framework_MockObject_MockObject */ @@ -81,6 +96,15 @@ protected function setUp() \Magento\AsynchronousOperations\Model\BulkStatus\CalculatedStatusSql::class ); $this->metadataPoolMock = $this->createMock(\Magento\Framework\EntityManager\MetadataPool::class); + $this->bulkDetailedFactory = $this->createPartialMock( + \Magento\AsynchronousOperations\Api\Data\DetailedBulkOperationsStatusInterfaceFactory ::class, + ['create'] + ); + $this->bulkShortFactory = $this->createPartialMock( + \Magento\AsynchronousOperations\Api\Data\BulkOperationsStatusInterfaceFactory::class, + ['create'] + ); + $this->entityManager = $this->createMock(\Magento\Framework\EntityManager\EntityManager::class); $this->entityMetadataMock = $this->createMock(\Magento\Framework\EntityManager\EntityMetadataInterface::class); $this->connectionMock = $this->createMock(\Magento\Framework\DB\Adapter\AdapterInterface::class); @@ -90,7 +114,10 @@ protected function setUp() $this->operationCollectionFactory, $this->resourceConnectionMock, $this->calculatedStatusSqlMock, - $this->metadataPoolMock + $this->metadataPoolMock, + $this->bulkDetailedFactory, + $this->bulkShortFactory, + $this->entityManager ); } @@ -151,12 +178,13 @@ public function getFailedOperationsByBulkIdDataProvider() { return [ [1, [1]], - [null, + [ + null, [ OperationInterface::STATUS_TYPE_RETRIABLY_FAILED, - OperationInterface::STATUS_TYPE_NOT_RETRIABLY_FAILED - ] - ] + OperationInterface::STATUS_TYPE_NOT_RETRIABLY_FAILED, + ], + ], ]; } diff --git a/app/code/Magento/AsynchronousOperations/Test/Unit/Model/Operation/DetailsTest.php b/app/code/Magento/AsynchronousOperations/Test/Unit/Model/Operation/DetailsTest.php index 7060a48e77390..f62e2b7f9d5ea 100644 --- a/app/code/Magento/AsynchronousOperations/Test/Unit/Model/Operation/DetailsTest.php +++ b/app/code/Magento/AsynchronousOperations/Test/Unit/Model/Operation/DetailsTest.php @@ -34,6 +34,7 @@ public function testGetDetails() $failedRetriable = 23; $failedNotRetriable = 45; $open = 303; + $rejected = 0; $expectedResult = [ 'operations_total' => $completed + $failedRetriable + $failedNotRetriable + $open, @@ -41,6 +42,8 @@ public function testGetDetails() 'operations_failed' => $failedRetriable + $failedNotRetriable, 'failed_retriable' => $failedRetriable, 'failed_not_retriable' => $failedNotRetriable, + 'rejected' => $rejected, + 'open' => $open, ]; $this->bulkStatusMock->method('getOperationsCountByBulkIdAndStatus') @@ -49,6 +52,7 @@ public function testGetDetails() [$uuid, OperationInterface::STATUS_TYPE_RETRIABLY_FAILED, $failedRetriable], [$uuid, OperationInterface::STATUS_TYPE_NOT_RETRIABLY_FAILED, $failedNotRetriable], [$uuid, OperationInterface::STATUS_TYPE_OPEN, $open], + [$uuid, OperationInterface::STATUS_TYPE_REJECTED, $rejected], ]); $result = $this->model->getDetails($uuid); diff --git a/app/code/Magento/AsynchronousOperations/Test/Unit/Model/OperationManagementTest.php b/app/code/Magento/AsynchronousOperations/Test/Unit/Model/OperationManagementTest.php index 32146b7b6eb19..0a4e5f2f3ecc3 100644 --- a/app/code/Magento/AsynchronousOperations/Test/Unit/Model/OperationManagementTest.php +++ b/app/code/Magento/AsynchronousOperations/Test/Unit/Model/OperationManagementTest.php @@ -40,10 +40,11 @@ protected function setUp() $this->entityManagerMock = $this->createMock(\Magento\Framework\EntityManager\EntityManager::class); $this->metadataPoolMock = $this->createMock(\Magento\Framework\EntityManager\MetadataPool::class); $this->operationFactoryMock = $this->createPartialMock( - \Magento\AsynchronousOperations\Api\Data\OperationInterfaceFactory::class, + \Magento\AsynchronousOperations\Api\Data\DetailedOperationStatusInterfaceFactory::class, ['create'] ); - $this->operationMock = $this->createMock(\Magento\AsynchronousOperations\Api\Data\OperationInterface::class); + $this->operationMock = + $this->createMock(\Magento\AsynchronousOperations\Api\Data\DetailedOperationStatusInterface::class); $this->loggerMock = $this->createMock(\Psr\Log\LoggerInterface::class); $this->model = new \Magento\AsynchronousOperations\Model\OperationManagement( $this->entityManagerMock, diff --git a/app/code/Magento/AsynchronousOperations/Test/Unit/Model/StatusMapperTest.php b/app/code/Magento/AsynchronousOperations/Test/Unit/Model/StatusMapperTest.php index 87d2bbac3cadc..89fa80de36378 100644 --- a/app/code/Magento/AsynchronousOperations/Test/Unit/Model/StatusMapperTest.php +++ b/app/code/Magento/AsynchronousOperations/Test/Unit/Model/StatusMapperTest.php @@ -73,7 +73,8 @@ public function testBulkSummaryStatusToOperationStatus() $this->model->bulkSummaryStatusToOperationStatus(BulkSummaryInterface::FINISHED_WITH_FAILURE), [ OperationInterface::STATUS_TYPE_NOT_RETRIABLY_FAILED, - OperationInterface::STATUS_TYPE_RETRIABLY_FAILED + OperationInterface::STATUS_TYPE_RETRIABLY_FAILED, + OperationInterface::STATUS_TYPE_REJECTED ] ); diff --git a/app/code/Magento/AsynchronousOperations/composer.json b/app/code/Magento/AsynchronousOperations/composer.json index 7a4e7f6aad69c..3acb92710e62b 100644 --- a/app/code/Magento/AsynchronousOperations/composer.json +++ b/app/code/Magento/AsynchronousOperations/composer.json @@ -5,20 +5,19 @@ "sort-packages": true }, "require": { - "magento/framework": "100.3.*", - "magento/framework-bulk": "100.3.*", - "magento/module-authorization": "100.3.*", - "magento/module-backend": "100.3.*", - "magento/module-ui": "100.3.*", - "magento/module-user": "100.3.*", - "php": "7.0.2|7.0.4|~7.0.6|~7.1.0" + "magento/framework": "*", + "magento/framework-bulk": "*", + "magento/module-authorization": "*", + "magento/module-backend": "*", + "magento/module-ui": "*", + "magento/module-user": "*", + "php": "~7.1.3||~7.2.0" }, "suggest": { - "magento/module-admin-notification": "100.3.*", - "magento/module-logging": "100.3.*" + "magento/module-admin-notification": "*", + "magento/module-logging": "*" }, "type": "magento2-module", - "version": "100.3.0-dev", "license": [ "proprietary" ], diff --git a/app/code/Magento/AsynchronousOperations/etc/db_schema.xml b/app/code/Magento/AsynchronousOperations/etc/db_schema.xml index b986b98abf3ad..1b99ce9a2805f 100644 --- a/app/code/Magento/AsynchronousOperations/etc/db_schema.xml +++ b/app/code/Magento/AsynchronousOperations/etc/db_schema.xml @@ -37,6 +37,8 @@ comment="Name of the related message queue topic"/> + + + + + + - + magento_operation id @@ -74,6 +79,29 @@ - + + + + + + Magento\AsynchronousOperations\Model\MassPublisher + Magento\AsynchronousOperations\Model\MassPublisher + + + + + + + Magento\AsynchronousOperations\Model\VirtualType\PublisherPool + + + + + Magento\AsynchronousOperations\Model\VirtualType\BulkManagement + + + + diff --git a/app/code/Magento/AsynchronousOperations/etc/webapi.xml b/app/code/Magento/AsynchronousOperations/etc/webapi.xml new file mode 100644 index 0000000000000..253dedd1c7a0c --- /dev/null +++ b/app/code/Magento/AsynchronousOperations/etc/webapi.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/code/Magento/Authorization/Model/ResourceModel/Role.php b/app/code/Magento/Authorization/Model/ResourceModel/Role.php index 633ae741b44a1..48fe65e7f8b92 100644 --- a/app/code/Magento/Authorization/Model/ResourceModel/Role.php +++ b/app/code/Magento/Authorization/Model/ResourceModel/Role.php @@ -68,6 +68,7 @@ protected function _beforeSave(\Magento\Framework\Model\AbstractModel $role) } if (!$role->getTreeLevel()) { + $treeLevel = 0; if ($role->getPid() > 0) { $select = $this->getConnection()->select()->from( $this->getMainTable(), @@ -79,8 +80,6 @@ protected function _beforeSave(\Magento\Framework\Model\AbstractModel $role) $binds = ['pid' => (int)$role->getPid()]; $treeLevel = $this->getConnection()->fetchOne($select, $binds); - } else { - $treeLevel = 0; } $role->setTreeLevel($treeLevel + 1); diff --git a/app/code/Magento/Authorization/composer.json b/app/code/Magento/Authorization/composer.json index 65e0d2a57e36d..5f5e7c62ef83b 100644 --- a/app/code/Magento/Authorization/composer.json +++ b/app/code/Magento/Authorization/composer.json @@ -5,12 +5,11 @@ "sort-packages": true }, "require": { - "php": "7.0.2|7.0.4|~7.0.6|~7.1.0", - "magento/framework": "100.3.*", - "magento/module-backend": "100.3.*" + "php": "~7.1.3||~7.2.0", + "magento/framework": "*", + "magento/module-backend": "*" }, "type": "magento2-module", - "version": "100.3.0-dev", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Authorizenet/composer.json b/app/code/Magento/Authorizenet/composer.json index 89d3ba8045a40..31f2295da4307 100644 --- a/app/code/Magento/Authorizenet/composer.json +++ b/app/code/Magento/Authorizenet/composer.json @@ -5,21 +5,20 @@ "sort-packages": true }, "require": { - "php": "7.0.2|7.0.4|~7.0.6|~7.1.0", - "magento/framework": "100.3.*", - "magento/module-backend": "100.3.*", - "magento/module-catalog": "101.2.*", - "magento/module-checkout": "100.3.*", - "magento/module-payment": "100.3.*", - "magento/module-quote": "100.3.*", - "magento/module-sales": "100.3.*", - "magento/module-store": "100.3.*" + "php": "~7.1.3||~7.2.0", + "magento/framework": "*", + "magento/module-backend": "*", + "magento/module-catalog": "*", + "magento/module-checkout": "*", + "magento/module-payment": "*", + "magento/module-quote": "*", + "magento/module-sales": "*", + "magento/module-store": "*" }, "suggest": { - "magento/module-config": "100.3.*" + "magento/module-config": "*" }, "type": "magento2-module", - "version": "100.3.0-dev", "license": [ "proprietary" ], diff --git a/app/code/Magento/Backend/Block/Menu.php b/app/code/Magento/Backend/Block/Menu.php index d6bdeb4ea8968..7d86497288a69 100644 --- a/app/code/Magento/Backend/Block/Menu.php +++ b/app/code/Magento/Backend/Block/Menu.php @@ -352,7 +352,7 @@ protected function _addSubMenu($menuItem, $level, $limit, $id = null) return $output; } $output .= '