diff --git a/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/MarkAsRead.php b/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/MarkAsRead.php
index 79f69ab5da8..6b5e0681139 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 9e61b8ff4b8..9ae4a7cdac0 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 94c7d955f59..06659b8452c 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 17f911339cb..f0724a9587c 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/AdvancedPricingImportExport/Model/Export/AdvancedPricing.php b/app/code/Magento/AdvancedPricingImportExport/Model/Export/AdvancedPricing.php
index 7ddd5e3bb2a..9047cc6481e 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,40 +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/AsynchronousOperations/Api/BulkStatusInterface.php b/app/code/Magento/AsynchronousOperations/Api/BulkStatusInterface.php
new file mode 100644
index 00000000000..88db2d6d801
--- /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 00000000000..5fc164ec833
--- /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 dda3ed3b673..7bdf8a5b7d4 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 00000000000..334abb33fd1
--- /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 00000000000..c6aa99e6720
--- /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 00000000000..de0f89a7165
--- /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 94ebbdca445..4abbde4c360 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 00000000000..b493e0bb663
--- /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 00000000000..28bc8141a8e
--- /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 00000000000..5f0f8e28f9f
--- /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 00000000000..a7c3eac3490
--- /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\WebapiAsync\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 dbfce3ccd8b..70cc9f0ebc5 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 398934f0933..d248f9c3e92 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 54b8a767584..ce780a4ba85 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 00000000000..6826c34fd35
--- /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 00000000000..5c975bd1a9a
--- /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 00000000000..54e65cc3470
--- /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 76c7820fa99..8457a641ed9 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 3ad260cf26a..e5aee6d2f59 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 cda55161ab8..7a2f7941f9c 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 7060a48e773..f62e2b7f9d5 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 32146b7b6eb..0a4e5f2f3ec 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 87d2bbac3ca..89fa80de363 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/etc/db_schema.xml b/app/code/Magento/AsynchronousOperations/etc/db_schema.xml
index b986b98abf3..1b99ce9a280 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 00000000000..253dedd1c7a
--- /dev/null
+++ b/app/code/Magento/AsynchronousOperations/etc/webapi.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Backend/Block/Menu.php b/app/code/Magento/Backend/Block/Menu.php
index d6bdeb4ea89..7d86497288a 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 .= '