" or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process.
\ No newline at end of file
diff --git a/app/code/Magento/DownloadableImportExport/Model/Import/Product/Type/Downloadable.php b/app/code/Magento/DownloadableImportExport/Model/Import/Product/Type/Downloadable.php
new file mode 100644
index 0000000000000..f8132f26e1e96
--- /dev/null
+++ b/app/code/Magento/DownloadableImportExport/Model/Import/Product/Type/Downloadable.php
@@ -0,0 +1,856 @@
+ 'Options for downloadable products not found',
+ self::ERROR_GROUP_TITLE_NOT_FOUND => 'Group titles not found for downloadable products',
+ self::ERROR_OPTION_NO_TITLE => 'Option no title',
+ self::ERROR_MOVE_FILE => 'Error move file',
+ self::ERROR_COLS_IS_EMPTY => 'Missing sample and links data for the downloadable product'
+ ];
+
+ /**
+ * Entity model parameters.
+ *
+ * @var array
+ */
+ protected $parameters = [];
+
+ /**
+ * Ids products
+ *
+ * @var array
+ */
+ protected $productIds = [];
+
+ /**
+ * Array of cached options.
+ *
+ * @var array
+ */
+ protected $cachedOptions = [
+ 'link' => [],
+ 'sample' => []
+ ];
+
+ /**
+ * Instance of empty sample
+ *
+ * @var array
+ */
+ protected $dataSample = [
+ 'sample_id' => null,
+ 'product_id' => null,
+ 'sample_url' => null,
+ 'sample_file' => null,
+ 'sample_type' => null,
+ 'sort_order' => self::DEFAULT_SORT_ORDER
+ ];
+
+ /**
+ * Instance of empty sample title
+ *
+ * @var array
+ */
+ protected $dataSampleTitle = [
+ 'sample_id' => null,
+ 'store_id' => Store::DEFAULT_STORE_ID,
+ 'title' => null
+ ];
+
+ /**
+ * Instance of empty link
+ *
+ * @var array
+ */
+ protected $dataLink = [
+ 'link_id' => null,
+ 'product_id' => null,
+ 'sort_order' => self::DEFAULT_SORT_ORDER,
+ 'number_of_downloads' => self::DEFAULT_NUMBER_OF_DOWNLOADS,
+ 'is_shareable' => self::DEFAULT_IS_SHAREABLE,
+ 'link_url' => null,
+ 'link_file' => null,
+ 'link_type' => null,
+ 'sample_url' => null,
+ 'sample_file' => null,
+ 'sample_type' => null
+ ];
+
+ /**
+ * Instance of empty link title
+ *
+ * @var array
+ */
+ protected $dataLinkTitle = [
+ 'link_id' => null,
+ 'store_id' => Store::DEFAULT_STORE_ID,
+ 'title' => null
+ ];
+
+ /**
+ * Instance of empty link price
+ *
+ * @var array
+ */
+ protected $dataLinkPrice = [
+ 'price_id' => null,
+ 'link_id' => null,
+ 'website_id' => self::DEFAULT_WEBSITE_ID,
+ 'price' => null
+ ];
+
+ /**
+ * Option link mapping.
+ *
+ * @var array
+ */
+ protected $optionLinkMapping = [
+ 'sortorder' => 'sort_order',
+ 'downloads' => 'number_of_downloads',
+ 'shareable' => 'is_shareable',
+ 'url' => 'link_url',
+ 'file' => 'link_file',
+ ];
+
+ /**
+ * Option sample mapping.
+ *
+ * @var array
+ */
+ protected $optionSampleMapping = [
+ 'sortorder' => 'sort_order',
+ 'url' => 'sample_url',
+ 'file' => 'sample_file',
+ ];
+
+ /**
+ * Num row parsing file
+ */
+ protected $rowNum;
+
+ /**
+ * @var \Magento\DownloadableImportExport\Helper\Uploader
+ */
+ protected $uploaderHelper;
+
+ /**
+ * @var \Magento\DownloadableImportExport\Helper\Data
+ */
+ protected $downloadableHelper;
+
+ /**
+ * Constructor
+ *
+ * @param \Magento\Eav\Model\Resource\Entity\Attribute\Set\CollectionFactory $attrSetColFac
+ * @param \Magento\Catalog\Model\Resource\Product\Attribute\CollectionFactory $prodAttrColFac
+ * @param \Magento\Framework\App\Resource $resource
+ * @param array $params
+ * @param \Magento\DownloadableImportExport\Helper\Uploader $uploaderHelper
+ * @param \Magento\DownloadableImportExport\Helper\Data $downloadableHelper
+ * @throws \Magento\Framework\Exception\LocalizedException
+ */
+ public function __construct(
+ \Magento\Eav\Model\Resource\Entity\Attribute\Set\CollectionFactory $attrSetColFac,
+ \Magento\Catalog\Model\Resource\Product\Attribute\CollectionFactory $prodAttrColFac,
+ \Magento\Framework\App\Resource $resource,
+ array $params,
+ \Magento\DownloadableImportExport\Helper\Uploader $uploaderHelper,
+ \Magento\DownloadableImportExport\Helper\Data $downloadableHelper
+ ) {
+ parent::__construct($attrSetColFac, $prodAttrColFac, $resource, $params);
+ $this->parameters = $this->_entityModel->getParameters();
+ $this->_resource = $resource;
+ $this->connection = $resource->getConnection('write');
+ $this->uploaderHelper = $uploaderHelper;
+ $this->downloadableHelper = $downloadableHelper;
+ }
+
+ /**
+ * Save product type specific data.
+ *
+ * @return \Magento\CatalogImportExport\Model\Import\Product\Type\AbstractType
+ */
+ public function saveData()
+ {
+ $newSku = $this->_entityModel->getNewSku();
+ while ($bunch = $this->_entityModel->getNextBunch()) {
+ foreach ($bunch as $rowNum => $rowData) {
+ if (!$this->_entityModel->isRowAllowedToImport($rowData, $rowNum)) {
+ continue;
+ }
+ $productData = $newSku[$rowData[\Magento\CatalogImportExport\Model\Import\Product::COL_SKU]];
+ if ($this->_type != $productData['type_id']) {
+ continue;
+ }
+ $this->parseOptions($rowData, $productData['entity_id']);
+ }
+ if (!empty($this->cachedOptions['sample']) || !empty($this->cachedOptions['link'])) {
+ $this->saveOptions();
+ $this->clear();
+ }
+ }
+ return $this;
+ }
+
+ /**
+ * Validate row attributes. Pass VALID row data ONLY as argument.
+ *
+ * @param array $rowData
+ * @param int $rowNum
+ * @param bool $isNewProduct Optional
+ *
+ * @return bool
+ */
+ public function isRowValid(array $rowData, $rowNum, $isNewProduct = true)
+ {
+ $this->rowNum = $rowNum;
+ $error = false;
+ if (!$this->downloadableHelper->isRowDownloadableNoValid($rowData)) {
+ $this->_entityModel->addRowError(self::ERROR_OPTIONS_NOT_FOUND, $this->rowNum);
+ $error = true;
+ }
+ if ($this->downloadableHelper->isRowDownloadableEmptyOptions($rowData)) {
+ $this->_entityModel->addRowError(self::ERROR_COLS_IS_EMPTY, $this->rowNum);
+ $error = true;
+ }
+ if ($this->isRowValidSample($rowData) || $this->isRowValidLink($rowData)) {
+ $error = true;
+ }
+ return !$error;
+ }
+
+ /**
+ * Validation sample options
+ *
+ * @param array $rowData
+ * @return bool
+ */
+ protected function isRowValidSample(array $rowData)
+ {
+ $result = false;
+ if (isset($rowData[self::COL_DOWNLOADABLE_SAMPLES])
+ && $rowData[self::COL_DOWNLOADABLE_SAMPLES] != ''
+ && $this->sampleGroupTitle($rowData) == '') {
+ $this->_entityModel->addRowError(self::ERROR_GROUP_TITLE_NOT_FOUND, $this->rowNum);
+ $result = true;
+ }
+ if (isset($rowData[self::COL_DOWNLOADABLE_SAMPLES])
+ && $rowData[self::COL_DOWNLOADABLE_SAMPLES] != '') {
+ $result = $this->isTitle($this->prepareSampleData($rowData[self::COL_DOWNLOADABLE_SAMPLES]));
+ }
+ return $result;
+ }
+
+ /**
+ * Validation links option
+ *
+ * @param array $rowData
+ * @return bool
+ */
+ protected function isRowValidLink(array $rowData)
+ {
+ $result = false;
+ if (isset($rowData[self::COL_DOWNLOADABLE_LINKS]) &&
+ $rowData[self::COL_DOWNLOADABLE_LINKS] != '' &&
+ $this->linksAdditionalAttributes($rowData, 'group_title', self::DEFAULT_GROUP_TITLE) == ''
+ ) {
+ $this->_entityModel->addRowError(self::ERROR_GROUP_TITLE_NOT_FOUND, $this->rowNum);
+ $result = true;
+ }
+ if (isset($rowData[self::COL_DOWNLOADABLE_LINKS]) &&
+ $rowData[self::COL_DOWNLOADABLE_LINKS] != ''
+ ) {
+ $result = $this->isTitle($this->prepareLinkData($rowData[self::COL_DOWNLOADABLE_LINKS]));
+ }
+ return $result;
+ }
+
+ /**
+ * Check isset title for all options
+ *
+ * @param array $options
+ * @return bool
+ */
+ protected function isTitle(array $options)
+ {
+ $result = false;
+ foreach ($options as $option) {
+ if (!array_key_exists('title', $option)) {
+ $this->_entityModel->addRowError(self::ERROR_OPTION_NO_TITLE, $this->rowNum);
+ $result = true;
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * Prepare attributes with default value for save.
+ *
+ * @param array $rowData
+ * @param bool $withDefaultValue
+ * @return array
+ */
+ public function prepareAttributesWithDefaultValueForSave(array $rowData, $withDefaultValue = true)
+ {
+ $resultAttrs = parent::prepareAttributesWithDefaultValueForSave($rowData, $withDefaultValue);
+ $resultAttrs = array_merge($resultAttrs, $this->addAdditionalAttributes($rowData));
+ return $resultAttrs;
+ }
+
+ /**
+ * Get additional attributes for dowloadable product.
+ *
+ * @param array $rowData
+ * @return array
+ */
+ protected function addAdditionalAttributes(array $rowData)
+ {
+ return [
+ 'samples_title' => $this->sampleGroupTitle($rowData),
+ 'links_title' => $this->linksAdditionalAttributes($rowData, 'group_title', self::DEFAULT_GROUP_TITLE),
+ 'links_purchased_separately' => $this->linksAdditionalAttributes(
+ $rowData,
+ 'purchased_separately',
+ self::DEFAULT_PURCHASED_SEPARATELY
+ )
+ ];
+ }
+
+ /**
+ * Get additional attributes for links
+ *
+ * @param array $rowData
+ * @param string $attribute
+ * @param mixed $defaultValue
+ * @return string
+ */
+ protected function linksAdditionalAttributes(array $rowData, $attribute, $defaultValue)
+ {
+ $result = $defaultValue;
+ if (isset($rowData[self::COL_DOWNLOADABLE_LINKS])) {
+ $options = explode(
+ \Magento\CatalogImportExport\Model\Import\Product::PSEUDO_MULTI_LINE_SEPARATOR,
+ $rowData[self::COL_DOWNLOADABLE_LINKS]
+ );
+ foreach ($options as $option) {
+ $arr = $this->parseLinkOption(explode($this->_entityModel->getMultipleValueSeparator(), $option));
+ if (isset($arr[$attribute])) {
+ $result = $arr[$attribute];
+ break;
+ }
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * Get group title for sample
+ *
+ * @param array $rowData
+ * @return string
+ */
+ protected function sampleGroupTitle(array $rowData)
+ {
+ $result = '';
+ if (isset($rowData[self::COL_DOWNLOADABLE_SAMPLES])) {
+ $options = explode(
+ \Magento\CatalogImportExport\Model\Import\Product::PSEUDO_MULTI_LINE_SEPARATOR,
+ $rowData[self::COL_DOWNLOADABLE_SAMPLES]
+ );
+ foreach ($options as $option) {
+ $arr = $this->parseSampleOption(explode($this->_entityModel->getMultipleValueSeparator(), $option));
+ if (isset($arr['group_title'])) {
+ $result = $arr['group_title'];
+ break;
+ }
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * Parse options for products
+ *
+ * @param array $rowData
+ * @param int $entityId
+ * @return $this
+ */
+ protected function parseOptions(array $rowData, $entityId)
+ {
+ $this->productIds[] = $entityId;
+ if (isset($rowData[self::COL_DOWNLOADABLE_LINKS])) {
+ $this->cachedOptions['link'] = array_merge(
+ $this->cachedOptions['link'],
+ $this->prepareLinkData($rowData[self::COL_DOWNLOADABLE_LINKS], $entityId)
+ );
+ }
+ if (isset($rowData[self::COL_DOWNLOADABLE_SAMPLES])) {
+ $this->cachedOptions['sample'] = array_merge(
+ $this->prepareSampleData($rowData[self::COL_DOWNLOADABLE_SAMPLES], $entityId),
+ $this->cachedOptions['sample']
+ );
+ }
+ return $this;
+ }
+
+ /**
+ * Get fill data options with key sample
+ *
+ * @param array $base
+ * @param array $options
+ * @return array
+ */
+ protected function fillDataSample(array $base, array $options)
+ {
+ $result = [];
+ $existingOptions = $this->connection->fetchAll(
+ $this->connection->select()->from(
+ $this->_resource->getTableName('downloadable_sample')
+ )->where(
+ 'product_id in (?)',
+ $this->productIds
+ )
+ );
+ foreach ($options as $option) {
+ $isExist = false;
+ foreach ($existingOptions as $existingOption) {
+ if ($option['sample_url'] == $existingOption['sample_url']
+ && $option['sample_file'] == $existingOption['sample_file']
+ && $option['sample_type'] == $existingOption['sample_type']
+ && $option['product_id'] == $existingOption['product_id']) {
+ $result[] = array_replace($this->dataSampleTitle, $option, $existingOption);
+ $isExist = true;
+ }
+ }
+ if (!$isExist) {
+ $result[] = array_replace($base, $option);
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * Get fill data options with key link
+ *
+ * @param array $base
+ * @param array $options
+ * @return array
+ */
+ protected function fillDataLink(array $base, array $options)
+ {
+ $result = [];
+ $existingOptions = $this->connection->fetchAll(
+ $this->connection->select()->from(
+ $this->_resource->getTableName('downloadable_link')
+ )->where(
+ 'product_id in (?)',
+ $this->productIds
+ )
+ );
+ foreach ($options as $option) {
+ $existOption = $this->downloadableHelper->fillExistOptions($base, $option, $existingOptions);
+ if (empty($existOption)) {
+ $result[] = array_replace($base, $option);
+ } else {
+ $result[] = $existOption;
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * Get fill data options with key link
+ *
+ * @param array $options
+ * @return array
+ */
+ protected function fillDataTitleLink(array $options)
+ {
+ $result = [];
+ $existingOptions = $this->connection->fetchAll(
+ $this->connection->select()->from(
+ ['dl' => $this->_resource->getTableName('downloadable_link')],
+ [
+ 'link_id',
+ 'product_id',
+ 'sort_order',
+ 'number_of_downloads',
+ 'is_shareable',
+ 'link_url',
+ 'link_file',
+ 'link_type',
+ 'sample_url',
+ 'sample_file',
+ 'sample_type'
+ ]
+ )->joinLeft(
+ ['dlp' => $this->_resource->getTableName('downloadable_link_price')],
+ 'dl.link_id = dlp.link_id AND dlp.website_id=' . self::DEFAULT_WEBSITE_ID,
+ ['price_id', 'website_id']
+ )->where(
+ 'product_id in (?)',
+ $this->productIds
+ )
+ );
+ foreach ($options as $option) {
+ $existOption = $this->downloadableHelper->fillExistOptions($this->dataLinkTitle, $option, $existingOptions);
+ if (!empty($existOption)) {
+ $result['title'][] = $existOption;
+ }
+ $existOption = $this->downloadableHelper->fillExistOptions($this->dataLinkPrice, $option, $existingOptions);
+ if (!empty($existOption)) {
+ $result['price'][] = $existOption;
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * Upload all sample files
+ *
+ * @param array $options
+ * @return array
+ */
+ protected function uploadSampleFiles(array $options)
+ {
+ $result = [];
+ foreach ($options as $option) {
+ if ($option['sample_file'] !== null) {
+ $option['sample_file'] = $this->uploadDownloadableFiles($option['sample_file'], 'samples', true);
+ }
+ $result[] = $option;
+ }
+ return $result;
+ }
+
+ /**
+ * Upload all link files
+ *
+ * @param array $options
+ * @return array
+ */
+ protected function uploadLinkFiles(array $options)
+ {
+ $result = [];
+ foreach ($options as $option) {
+ if ($option['sample_file'] !== null) {
+ $option['sample_file'] = $this->uploadDownloadableFiles($option['sample_file'], 'link_samples', true);
+ }
+ if ($option['link_file'] !== null) {
+ $option['link_file'] = $this->uploadDownloadableFiles($option['link_file'], 'links', true);
+ }
+ $result[] = $option;
+ }
+ return $result;
+ }
+
+ /**
+ * Save options in base
+ *
+ * @return \Magento\CatalogImportExport\Model\Import\Product\Type\AbstractType
+ */
+ protected function saveOptions()
+ {
+ $options = $this->cachedOptions;
+ if (!empty($options['sample'])) {
+ $this->saveSampleOptions();
+ }
+ if (!empty($options['link'])) {
+ $this->saveLinkOptions();
+ }
+ return $this;
+ }
+
+ /**
+ * Save sample options
+ *
+ * @return $this
+ */
+ protected function saveSampleOptions()
+ {
+ $options['sample'] = $this->uploadSampleFiles($this->cachedOptions['sample']);
+ $dataSample = $this->fillDataSample($this->dataSample, $options['sample']);
+ $this->connection->insertOnDuplicate(
+ $this->_resource->getTableName('downloadable_sample'),
+ $this->downloadableHelper->prepareDataForSave($this->dataSample, $dataSample)
+ );
+ $titleSample = $this->fillDataSample($this->dataSampleTitle, $options['sample']);
+ $this->connection->insertOnDuplicate(
+ $this->_resource->getTableName('downloadable_sample_title'),
+ $this->downloadableHelper->prepareDataForSave($this->dataSampleTitle, $titleSample)
+ );
+ return $this;
+ }
+
+ /**
+ * Save link options
+ *
+ * @return $this
+ */
+ protected function saveLinkOptions()
+ {
+ $options['link'] = $this->uploadLinkFiles($this->cachedOptions['link']);
+ $dataLink = $this->fillDataLink($this->dataLink, $options['link']);
+ $this->connection->insertOnDuplicate(
+ $this->_resource->getTableName('downloadable_link'),
+ $this->downloadableHelper->prepareDataForSave($this->dataLink, $dataLink)
+ );
+ $dataLink = $this->fillDataTitleLink($options['link']);
+ $this->connection->insertOnDuplicate(
+ $this->_resource->getTableName('downloadable_link_title'),
+ $this->downloadableHelper->prepareDataForSave($this->dataLinkTitle, $dataLink['title'])
+ );
+ if (count($dataLink['price'])) {
+ $this->connection->insertOnDuplicate(
+ $this->_resource->getTableName('downloadable_link_price'),
+ $this->downloadableHelper->prepareDataForSave($this->dataLinkPrice, $dataLink['price'])
+ );
+ }
+ return $this;
+ }
+
+ /**
+ * Prepare string to array data sample
+ *
+ * @param string $rowCol
+ * @param int $entityId
+ * @return array
+ */
+ protected function prepareSampleData($rowCol, $entityId = null)
+ {
+ $result = [];
+ $options = explode(
+ \Magento\CatalogImportExport\Model\Import\Product::PSEUDO_MULTI_LINE_SEPARATOR,
+ $rowCol
+ );
+ foreach ($options as $option) {
+ $result[] = array_merge(
+ $this->dataSample,
+ ['product_id' => $entityId],
+ $this->parseSampleOption(explode($this->_entityModel->getMultipleValueSeparator(), $option))
+ );
+ }
+ return $result;
+ }
+
+ /**
+ * Prepare string to array data link
+ *
+ * @param string $rowCol
+ * @param string $entityId
+ * @return array
+ */
+ protected function prepareLinkData($rowCol, $entityId = null)
+ {
+ $result = [];
+ $options = explode(
+ \Magento\CatalogImportExport\Model\Import\Product::PSEUDO_MULTI_LINE_SEPARATOR,
+ $rowCol
+ );
+ foreach ($options as $option) {
+ $result[] = array_merge(
+ $this->dataLink,
+ ['product_id' => $entityId],
+ $this->parseLinkOption(explode($this->_entityModel->getMultipleValueSeparator(), $option))
+ );
+ }
+ return $result;
+ }
+
+ /**
+ * Parse the link option.
+ *
+ * @param array $values
+ * @return array
+ */
+ protected function parseLinkOption(array $values)
+ {
+ $option = [];
+ foreach ($values as $keyValue) {
+ $keyValue = trim($keyValue);
+ if ($pos = strpos($keyValue, self::PAIR_VALUE_SEPARATOR)) {
+ $key = substr($keyValue, 0, $pos);
+ $value = substr($keyValue, $pos + 1);
+ if ($key == 'sample') {
+ $option['sample_type'] = $this->downloadableHelper->getTypeByValue($value);
+ $option['sample_' . $option['sample_type']] = $value;
+ }
+ if ($key == self::URL_OPTION_VALUE || $key == self::FILE_OPTION_VALUE) {
+ $option['link_type'] = $key;
+ }
+ if ($key == 'downloads' && $value == 'unlimited') {
+ $value = 0;
+ }
+ if (isset($this->optionLinkMapping[$key])) {
+ $key = $this->optionLinkMapping[$key];
+ }
+ $option[$key] = $value;
+ }
+ }
+ return $option;
+ }
+
+ /**
+ * Parse the sample option.
+ *
+ * @param array $values
+ * @return array
+ */
+ protected function parseSampleOption($values)
+ {
+ $option = [];
+ foreach ($values as $keyValue) {
+ $keyValue = trim($keyValue);
+ if ($pos = strpos($keyValue, self::PAIR_VALUE_SEPARATOR)) {
+ $key = substr($keyValue, 0, $pos);
+ $value = substr($keyValue, $pos + 1);
+ if ($key == self::URL_OPTION_VALUE || $key == self::FILE_OPTION_VALUE) {
+ $option['sample_type'] = $key;
+ }
+ if (isset($this->optionSampleMapping[$key])) {
+ $key = $this->optionSampleMapping[$key];
+ }
+ $option[$key] = $value;
+ }
+ }
+ return $option;
+ }
+
+ /**
+ * Uploading files into the "downloadable/files" media folder.
+ * Return a new file name if the same file is already exists.
+ *
+ * @param string $fileName
+ * @param string $type
+ * @param bool $renameFileOff
+ * @return string
+ */
+ protected function uploadDownloadableFiles($fileName, $type = 'links', $renameFileOff = false)
+ {
+ try {
+ $res = $this->uploaderHelper->getUploader($type, $this->parameters)->move($fileName, $renameFileOff);
+ return $res['file'];
+ } catch (\Exception $e) {
+ $this->_entityModel->addRowError(self::ERROR_MOVE_FILE, $this->rowNum);
+ return '';
+ }
+ }
+
+ /**
+ * Clear cached values between bunches
+ *
+ * @return \Magento\CatalogImportExport\Model\Import\Product\Type\AbstractType
+ */
+ protected function clear()
+ {
+ $this->cachedOptions = [
+ 'link' => [],
+ 'sample' => []
+ ];
+ $this->productIds = [];
+ return $this;
+ }
+}
diff --git a/app/code/Magento/DownloadableImportExport/README.md b/app/code/Magento/DownloadableImportExport/README.md
new file mode 100644
index 0000000000000..0f9c66837519f
--- /dev/null
+++ b/app/code/Magento/DownloadableImportExport/README.md
@@ -0,0 +1 @@
+The Magento_DownloadableImportExport module handles the import and export of the downloadable products.
diff --git a/app/code/Magento/DownloadableImportExport/Test/Unit/Model/Import/Product/Type/DownloadableTest.php b/app/code/Magento/DownloadableImportExport/Test/Unit/Model/Import/Product/Type/DownloadableTest.php
new file mode 100644
index 0000000000000..93a353f68089b
--- /dev/null
+++ b/app/code/Magento/DownloadableImportExport/Test/Unit/Model/Import/Product/Type/DownloadableTest.php
@@ -0,0 +1,858 @@
+connectionMock = $this->getMock(
+ 'Magento\Framework\DB\Adapter\Pdo\Mysql',
+ ['select', 'fetchAll', 'fetchPairs', 'joinLeft', 'insertOnDuplicate', 'delete', 'quoteInto', 'fetchAssoc'],
+ [],
+ '',
+ false
+ );
+ $this->select = $this->getMock('Magento\Framework\DB\Select', [], [], '', false);
+ $this->select->expects($this->any())->method('from')->will($this->returnSelf());
+ $this->select->expects($this->any())->method('where')->will($this->returnSelf());
+ $this->select->expects($this->any())->method('joinLeft')->will($this->returnSelf());
+ $adapter = $this->getMock('Magento\Framework\DB\Adapter\Pdo\Mysql', [], [], '', false);
+ $adapter->expects($this->any())->method('quoteInto')->will($this->returnValue('query'));
+ $this->select->expects($this->any())->method('getAdapter')->willReturn($adapter);
+ $this->connectionMock->expects($this->any())->method('select')->will($this->returnValue($this->select));
+
+ $this->connectionMock->expects($this->any())->method('insertOnDuplicate')->willReturnSelf();
+ $this->connectionMock->expects($this->any())->method('delete')->willReturnSelf();
+ $this->connectionMock->expects($this->any())->method('quoteInto')->willReturn('');
+
+ //constructor arguments:
+ // 1. $attrSetColFac
+ $this->attrSetColFacMock = $this->getMock(
+ 'Magento\Eav\Model\Resource\Entity\Attribute\Set\CollectionFactory',
+ ['create'],
+ [],
+ '',
+ false
+ );
+ $this->attrSetColMock = $this->getMock(
+ 'Magento\Eav\Model\Resource\Entity\Attribute\Set\Collection',
+ ['setEntityTypeFilter'],
+ [],
+ '',
+ false
+ );
+ $this->attrSetColMock
+ ->expects($this->any())
+ ->method('setEntityTypeFilter')
+ ->will($this->returnValue([]));
+
+ // 2. $prodAttrColFac
+ $this->prodAttrColFacMock = $this->getMock(
+ 'Magento\Catalog\Model\Resource\Product\Attribute\CollectionFactory',
+ ['create'],
+ [],
+ '',
+ false
+ );
+
+ $attrCollection = $this->getMock(
+ '\Magento\Catalog\Model\Resource\Product\Attribute\Collection',
+ [],
+ [],
+ '',
+ false
+ );
+
+ $attrCollection->expects($this->any())->method('addFieldToFilter')->willReturn([]);
+ $this->prodAttrColFacMock->expects($this->any())->method('create')->will($this->returnValue($attrCollection));
+
+ // 3. $resource
+ $this->resourceMock = $this->getMock(
+ 'Magento\Framework\App\Resource',
+ ['getConnection', 'getTableName'],
+ [],
+ '',
+ false
+ );
+ $this->resourceMock->expects($this->any())->method('getConnection')->will(
+ $this->returnValue($this->connectionMock)
+ );
+ $this->resourceMock->expects($this->any())->method('getTableName')->will(
+ $this->returnValue('tableName')
+ );
+
+ // 4. $params
+ $this->entityModelMock = $this->getMock(
+ '\Magento\CatalogImportExport\Model\Import\Product',
+ [
+ 'addMessageTemplate',
+ 'getEntityTypeId',
+ 'getBehavior',
+ 'getNewSku',
+ 'getNextBunch',
+ 'isRowAllowedToImport',
+ 'getParameters',
+ 'addRowError'
+ ],
+ [],
+ '',
+ false
+ );
+
+ $this->entityModelMock->expects($this->any())->method('addMessageTemplate')->will($this->returnSelf());
+ $this->entityModelMock->expects($this->any())->method('getEntityTypeId')->will($this->returnValue(5));
+ $this->entityModelMock->expects($this->any())->method('getParameters')->will($this->returnValue([]));
+ $this->paramsArray = [
+ $this->entityModelMock,
+ 'downloadable'
+ ];
+
+ $this->uploaderMock = $this->getMock(
+ '\Magento\CatalogImportExport\Model\Import\Uploader',
+ ['move'],
+ [],
+ '',
+ false
+ );
+
+ // 6. $filesystem
+ $this->directoryWriteMock = $this->getMock('Magento\Framework\Filesystem\Directory\Write', [], [], '', false);
+
+ // 7. $fileHelper
+ $this->uploaderHelper = $this->getMock(
+ '\Magento\DownloadableImportExport\Helper\Uploader',
+ ['getUploader'],
+ [],
+ '',
+ false
+ );
+ $this->uploaderHelper->expects($this->any())->method('getUploader')->willReturn($this->uploaderMock);
+ $this->downloadableHelper = $this->getMock(
+ '\Magento\DownloadableImportExport\Helper\Data',
+ ['prepareDataForSave'],
+ [],
+ '',
+ false
+ );
+ $this->downloadableHelper->expects($this->any())->method('prepareDataForSave')->willReturn([]);
+ }
+
+ /**
+ * @dataProvider dataForSave
+ */
+ public function testSaveDataAppend($newSku, $bunch, $allowImport, $fetchResult)
+ {
+ $this->entityModelMock->expects($this->once())->method('getNewSku')->will($this->returnValue($newSku));
+ $this->entityModelMock->expects($this->at(1))->method('getNextBunch')->will($this->returnValue($bunch));
+ $this->entityModelMock->expects($this->at(2))->method('getNextBunch')->will($this->returnValue(null));
+ $this->entityModelMock->expects($this->any())->method('isRowAllowedToImport')->willReturn($allowImport);
+
+ $this->uploaderMock->expects($this->any())->method('setTmpDir')->willReturn(true);
+ $this->uploaderMock->expects($this->any())->method('setDestDir')->with('pub/media/')->willReturn(true);
+
+ $this->connectionMock->expects($this->any())->method('fetchAll')->with(
+ $this->select
+ )->will($this->onConsecutiveCalls(
+ [
+ [
+ 'attribute_set_name' => '1',
+ 'attribute_id' => '1',
+ ],
+ [
+ 'attribute_set_name' => '2',
+ 'attribute_id' => '2',
+ ],
+ ],
+ $fetchResult['sample'],
+ $fetchResult['sample'],
+ $fetchResult['link'],
+ $fetchResult['link']
+ ));
+
+ $downloadableModelMock = $this->objectManagerHelper->getObject(
+ '\Magento\DownloadableImportExport\Model\Import\Product\Type\Downloadable',
+ [
+ 'attrSetColFac' => $this->attrSetColFacMock,
+ 'prodAttrColFac' => $this->prodAttrColFacMock,
+ 'resource' => $this->resourceMock,
+ 'params' => $this->paramsArray,
+ 'uploaderHelper' => $this->uploaderHelper,
+ 'downloadableHelper' => $this->downloadableHelper
+ ]
+ );
+
+ $downloadableModelMock->saveData();
+ }
+
+ /**
+ * Data for method testSaveDataAppend
+ *
+ * @return array
+ * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
+ */
+ public function dataForSave()
+ {
+ return [
+ [
+ 'newSku' => [
+ 'downloadablesku1' => [
+ 'entity_id' => '25',
+ 'type_id' => 'downloadable',
+ 'attr_set_id' => '4',
+ 'attr_set_code' => 'Default',
+ ],
+ ],
+ 'bunch' => [
+ [
+ 'sku' => 'downloadablesku1',
+ 'product_type' => 'downloadable',
+ 'name' => 'Downloadable Product 1',
+ 'downloadable_samples' => 'group_title=Group Title Samples, title=Title 1, file=media/file.mp4'
+ .',sortorder=1|group_title=Group Title, title=Title 2, url=media/file2.mp4,sortorder=0',
+ 'downloadable_links' => 'group_title=Group Title Links, title=Title 1, price=10,'
+ .' downloads=unlimited, file=media/file_link.mp4,sortorder=1|group_title=Group Title,'
+ .'title=Title 2, price=10, downloads=unlimited, url=media/file2.mp4,sortorder=0',
+ ],
+ ],
+ 'allowImport' => true,
+ [
+ 'sample' => [
+ [
+ 'sample_id' => '65',
+ 'product_id' => '25',
+ 'sample_url' => null,
+ 'sample_file' => '',
+ 'sample_type' => 'file',
+ 'sort_order' => '1',
+ ],
+ [
+ 'sample_id' => '66',
+ 'product_id' => '25',
+ 'sample_url' => 'media/file2.mp4',
+ 'sample_file' => null,
+ 'sample_type' => 'url',
+ 'sort_order' => '0',
+ ]
+ ],
+ 'link' => [
+ [
+ 'link_id' => '65',
+ 'product_id' => '25',
+ 'sort_order' => '1',
+ 'number_of_downloads' => '0',
+ 'is_shareable' => '2',
+ 'link_url' => null,
+ 'link_file' => '',
+ 'link_type' => 'file',
+ 'sample_url' => null,
+ 'sample_file' => null,
+ 'sample_type' => null,
+ ],
+ [
+ 'link_id' => '66',
+ 'product_id' => '25',
+ 'sort_order' => '0',
+ 'number_of_downloads' => '0',
+ 'is_shareable' => '2',
+ 'link_url' => 'media/file2.mp4',
+ 'link_file' => null,
+ 'link_type' => 'url',
+ 'sample_url' => null,
+ 'sample_file' => null,
+ 'sample_type' => null,
+ ]
+ ]
+ ]
+ ],
+ [
+ 'newSku' => [
+ 'downloadablesku2' => [
+ 'entity_id' => '25',
+ 'type_id' => 'downloadable',
+ 'attr_set_id' => '4',
+ 'attr_set_code' => 'Default',
+ ],
+ ],
+ 'bunch' => [
+ [
+ 'sku' => 'downloadablesku2',
+ 'product_type' => 'downloadable',
+ 'name' => 'Downloadable Product 1',
+ 'downloadable_samples' => 'group_title=Group Title Samples, title=Title 1, file=media/file.mp4'
+ .',sortorder=1|group_title=Group Title, title=Title 2, url=media/file2.mp4,sortorder=0',
+ 'downloadable_links' => 'group_title=Group Title Links, title=Title 1, price=10,'
+ .' downloads=unlimited, file=media/file_link.mp4,sortorder=1|group_title=Group Title, '
+ .'title=Title 2, price=10, downloads=unlimited, url=media/file2.mp4,sortorder=0',
+ ],
+ ],
+ 'allowImport' => false,
+ ['sample' => [], 'link' => []]
+ ],
+ [
+ 'newSku' => [
+ 'downloadablesku3' => [
+ 'entity_id' => '25',
+ 'type_id' => 'simple',
+ 'attr_set_id' => '4',
+ 'attr_set_code' => 'Default',
+ ],
+ ],
+ 'bunch' => [
+ [
+ 'sku' => 'downloadablesku3',
+ 'product_type' => 'downloadable',
+ 'name' => 'Downloadable Product 1',
+ 'downloadable_samples' => 'group_title=Group Title Samples, title=Title 1, file=media/file.mp4,'
+ .'sortorder=1|group_title=Group Title, title=Title 2, url=media/file2.mp4,sortorder=0',
+ 'downloadable_links' => 'group_title=Group Title Links, title=Title 1, price=10,'
+ .' downloads=unlimited, file=media/file_link.mp4,sortorder=1|group_title=Group Title,'
+ .' title=Title 2, price=10, downloads=unlimited, url=media/file2.mp4,sortorder=0',
+ ],
+ ],
+ 'allowImport' => true,
+ ['sample' => [], 'link' => []]
+ ],
+ [
+ 'newSku' => [
+ 'downloadablesku4' => [
+ 'entity_id' => '25',
+ 'type_id' => 'downloadable',
+ 'attr_set_id' => '4',
+ 'attr_set_code' => 'Default',
+ ],
+ ],
+ 'bunch' => [
+ [
+ 'sku' => 'downloadablesku4',
+ 'product_type' => 'downloadable',
+ 'name' => 'Downloadable Product 1',
+ 'downloadable_samples' => 'group_title=Group Title Samples, title=Title 1, file=media/file.mp4,'
+ .'sortorder=1|group_title=Group Title, title=Title 2, url=media/file2.mp4,sortorder=0',
+ 'downloadable_links' => 'group_title=Group Title Links, title=Title 1, price=10,'
+ .' downloads=unlimited, file=media/file_link.mp4,sortorder=1|group_title=Group Title,'
+ .' title=Title 2, price=10, downloads=unlimited, url=media/file2.mp4,sortorder=0',
+ ],
+ ],
+ 'allowImport' => true,
+ [
+ 'sample' => [
+ [
+ 'sample_id' => '65',
+ 'product_id' => '25',
+ 'sample_url' => null,
+ 'sample_file' => '',
+ 'sample_type' => 'file',
+ 'sort_order' => '1',
+ ],
+ [
+ 'sample_id' => '66',
+ 'product_id' => '25',
+ 'sample_url' => 'media/some_another_file.mp4',
+ 'sample_file' => null,
+ 'sample_type' => 'url',
+ 'sort_order' => '0',
+ ]
+ ],
+ 'link' => [
+ [
+ 'link_id' => '65',
+ 'product_id' => '25',
+ 'sort_order' => '1',
+ 'number_of_downloads' => '0',
+ 'is_shareable' => '2',
+ 'link_url' => null,
+ 'link_file' => '',
+ 'link_type' => 'file',
+ 'sample_url' => null,
+ 'sample_file' => null,
+ 'sample_type' => null,
+ ],
+ [
+ 'link_id' => '66',
+ 'product_id' => '25',
+ 'sort_order' => '0',
+ 'number_of_downloads' => '0',
+ 'is_shareable' => '2',
+ 'link_url' => 'media/some_another_file.mp4',
+ 'link_file' => null,
+ 'link_type' => 'url',
+ 'sample_url' => null,
+ 'sample_file' => null,
+ 'sample_type' => null,
+ ]
+ ]
+ ]
+ ],
+ [
+ 'newSku' => [
+ 'downloadablesku5' => [
+ 'entity_id' => '25',
+ 'type_id' => 'downloadable',
+ 'attr_set_id' => '4',
+ 'attr_set_code' => 'Default',
+ ],
+ ],
+ 'bunch' => [
+ [
+ 'sku' => 'downloadablesku5',
+ 'product_type' => 'downloadable',
+ 'name' => 'Downloadable Product 1',
+ 'downloadable_samples' => 'group_title=Group Title Samples, title=Title 1, file=media/file.mp4,'
+ .'sortorder=1|group_title=Group Title, title=Title 2, url=media/file2.mp4,sortorder=0',
+ 'downloadable_links' => 'group_title=Group Title, title=Title 2, price=10, downloads=unlimited,'
+ .' url=http://www.sample.com/pic.jpg,sortorder=0,sample=http://www.sample.com/pic.jpg,'
+ .'purchased_separately=1,shareable=1|group_title=Group Title, title=Title 2, price=10, '
+ .'downloads=unlimited, url=media/file2.mp4,sortorder=0,sample=media/file2mp4',
+ ],
+ ],
+ 'allowImport' => true,
+ [
+ 'sample' => [
+ [
+ 'sample_id' => '65',
+ 'product_id' => '25',
+ 'sample_url' => null,
+ 'sample_file' => '',
+ 'sample_type' => 'file',
+ 'sort_order' => '1',
+ ],
+ [
+ 'sample_id' => '66',
+ 'product_id' => '25',
+ 'sample_url' => 'media/file2.mp4',
+ 'sample_file' => null,
+ 'sample_type' => 'url',
+ 'sort_order' => '0',
+ ]
+ ],
+ 'link' => [
+ [
+ 'link_id' => '65',
+ 'product_id' => '25',
+ 'sort_order' => '1',
+ 'number_of_downloads' => '0',
+ 'is_shareable' => '1',
+ 'link_url' => 'http://www.sample.com/pic.jpg',
+ 'link_file' => null,
+ 'link_type' => 'url',
+ 'sample_url' => 'http://www.sample.com/pic.jpg',
+ 'sample_file' => null,
+ 'sample_type' => 'url',
+ ],
+ [
+ 'link_id' => '66',
+ 'product_id' => '25',
+ 'sort_order' => '0',
+ 'number_of_downloads' => '0',
+ 'is_shareable' => '2',
+ 'link_url' => 'media/file2.mp4',
+ 'link_file' => null,
+ 'link_type' => 'url',
+ 'sample_url' => null,
+ 'sample_file' => 'f/i/file.png',
+ 'sample_type' => 'file',
+ ]
+ ]
+ ]
+ ],
+ ];
+ }
+
+ /**
+ * @dataProvider isRowValidData
+ */
+ public function testIsRowValid(array $rowData, $rowNum, $isNewProduct = true)
+ {
+ $this->connectionMock->expects($this->any())->method('fetchAll')->with(
+ $this->select
+ )->willReturnOnConsecutiveCalls(
+ [
+ [
+ 'attribute_set_name' => '1',
+ 'attribute_id' => '1',
+ ],
+ [
+ 'attribute_set_name' => '2',
+ 'attribute_id' => '2',
+ ],
+ ]
+ );
+ $this->downloadableModelMock = $this->objectManagerHelper->getObject(
+ '\Magento\DownloadableImportExport\Model\Import\Product\Type\Downloadable',
+ [
+ 'attrSetColFac' => $this->attrSetColFacMock,
+ 'prodAttrColFac' => $this->prodAttrColFacMock,
+ 'resource' => $this->resourceMock,
+ 'params' => $this->paramsArray,
+ 'uploaderHelper' => $this->uploaderHelper,
+ 'downloadableHelper' => $this->downloadableHelper
+ ]
+ );
+ $this->downloadableModelMock->isRowValid($rowData, $rowNum, $isNewProduct);
+ }
+
+ /**
+ * Data for method testIsRowValid
+ *
+ * @return array
+ * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
+ */
+ public function isRowValidData()
+ {
+ return [
+ [
+ [
+ 'sku' => 'downloadablesku1',
+ 'product_type' => 'downloadable',
+ 'name' => 'Downloadable Product 1',
+ 'downloadable_samples' => 'group_title=Group Title Samples, title=Title 1, file=media/file.mp4,'
+ .'sortorder=1|group_title=Group Title, title=Title 2, url=media/file2.mp4,sortorder=0',
+ 'downloadable_links' => 'group_title=Group Title Links, title=Title 1, price=10, '
+ .'downloads=unlimited, file=media/file_link.mp4,sortorder=1|group_title=Group Title, '
+ .'title=Title 2, price=10, downloads=unlimited, url=media/file2.mp4,sortorder=0',
+ ],
+ 0,
+ true
+ ],
+ [
+ [
+ 'sku' => 'downloadablesku12',
+ 'product_type' => 'downloadable',
+ 'name' => 'Downloadable Product 2',
+ 'downloadable_samples' => 'group_title=Group Title Samples, title=Title 1, file=media/file.mp4'
+ .',sortorder=1|group_title=Group Title, title=Title 2, url=media/file2.mp4,sortorder=0',
+ 'downloadable_links' => 'group_title=Group Title Links, title=Title 1, price=10,'
+ .' downloads=unlimited, file=media/file.mp4,sortorder=1|group_title=Group Title, title=Title 2,'
+ .' price=10, downloads=unlimited, url=media/file2.mp4,sortorder=0',
+ ],
+ 1,
+ true
+ ],
+ [
+ [
+ 'sku' => 'downloadablesku12',
+ 'product_type' => 'downloadable',
+ 'name' => 'Downloadable Product 2',
+ ],
+ 2,
+ true
+ ],
+ [
+ [
+ 'sku' => 'downloadablesku12',
+ 'product_type' => 'downloadable',
+ 'name' => 'Downloadable Product 2',
+ 'downloadable_samples' => 'title=Title 1, file=media/file.mp4,sortorder=1|title=Title 2,'
+ .' url=media/file2.mp4,sortorder=0',
+ 'downloadable_links' => 'title=Title 1, price=10, downloads=unlimited, file=media/file.mp4,'
+ .'sortorder=1|group_title=Group Title, title=Title 2, price=10, downloads=unlimited,'
+ .' url=media/file2.mp4,sortorder=0',
+ ],
+ 3,
+ true
+ ],
+ [
+ [
+ 'sku' => 'downloadablesku12',
+ 'product_type' => 'downloadable',
+ 'name' => 'Downloadable Product 2',
+ 'downloadable_samples' => 'file=media/file.mp4,sortorder=1|group_title=Group Title, '
+ .'url=media/file2.mp4,sortorder=0',
+ 'downloadable_links' => 'title=Title 1, price=10, downloads=unlimited, file=media/file.mp4,'
+ .'sortorder=1|group_title=Group Title, title=Title 2, price=10, downloads=unlimited,'
+ .' url=media/file2.mp4,sortorder=0',
+ ],
+ 4,
+ true
+ ],
+ [ //empty group title samples
+ [
+ 'sku' => 'downloadablesku12',
+ 'product_type' => 'downloadable',
+ 'name' => 'Downloadable Product 2',
+ 'downloadable_samples' => 'group_title=, title=Title 1, file=media/file.mp4,sortorder=1'
+ .'|group_title=, title=Title 2, url=media/file2.mp4,sortorder=0',
+ 'downloadable_links' => 'group_title=Group Title Links, title=Title 1, price=10,'
+ .' downloads=unlimited, file=media/file_link.mp4,sortorder=1|group_title=Group Title,'
+ .' title=Title 2, price=10, downloads=unlimited, url=media/file2.mp4,sortorder=0',
+ ],
+ 5,
+ true
+ ],
+ [ //empty group title links
+ [
+ 'sku' => 'downloadablesku12',
+ 'product_type' => 'downloadable',
+ 'name' => 'Downloadable Product 2',
+ 'downloadable_samples' => 'group_title=Group Title Samples, title=Title 1, file=media/file.mp4,'
+ .'sortorder=1|group_title=Group Title, title=Title 2, url=media/file2.mp4,sortorder=0',
+ 'downloadable_links' => 'group_title=, title=Title 1, price=10, downloads=unlimited, '
+ .'file=media/file_link.mp4,sortorder=1|group_title=, title=Title 2, price=10, '
+ .'downloads=unlimited, url=media/file2.mp4,sortorder=0',
+ ],
+ 6,
+ true
+ ],
+ [
+ [
+ 'sku' => 'downloadablesku12',
+ 'product_type' => 'downloadable',
+ 'name' => 'Downloadable Product 2',
+ 'downloadable_samples' => '',
+ 'downloadable_links' => '',
+ ],
+ 7,
+ true
+ ],
+ ];
+ }
+
+ /**
+ * @dataProvider dataForUploaderDir
+ */
+ public function testSetUploaderDirFalse($newSku, $bunch, $allowImport)
+ {
+ $this->connectionMock->expects($this->any())->method('fetchAll')->with(
+ $this->select
+ )->willReturnOnConsecutiveCalls(
+ [
+ [
+ 'attribute_set_name' => '1',
+ 'attribute_id' => '1',
+ ],
+ [
+ 'attribute_set_name' => '2',
+ 'attribute_id' => '2',
+ ],
+ ]
+ );
+ $this->downloadableModelMock = $this->objectManagerHelper->getObject(
+ '\Magento\DownloadableImportExport\Model\Import\Product\Type\Downloadable',
+ [
+ 'attrSetColFac' => $this->attrSetColFacMock,
+ 'prodAttrColFac' => $this->prodAttrColFacMock,
+ 'resource' => $this->resourceMock,
+ 'params' => $this->paramsArray,
+ 'uploaderHelper' => $this->uploaderHelper,
+ 'downloadableHelper' => $this->downloadableHelper
+ ]
+ );
+ $this->entityModelMock->expects($this->once())->method('getNewSku')->will($this->returnValue($newSku));
+ $this->entityModelMock->expects($this->at(1))->method('getNextBunch')->will($this->returnValue($bunch));
+ $this->entityModelMock->expects($this->at(2))->method('getNextBunch')->will($this->returnValue(null));
+ $this->entityModelMock->expects($this->any())->method('isRowAllowedToImport')->willReturn($allowImport);
+ $exception = new \Magento\Framework\Exception\LocalizedException(new \Magento\Framework\Phrase('Error'));
+ $this->setExpectedException('\Magento\Framework\Exception\LocalizedException');
+ $this->setExpectedException('\Exception');
+ $this->uploaderMock->expects($this->any())->method('move')->will($this->throwException($exception));
+ $this->downloadableModelMock->saveData();
+ }
+
+ /**
+ * Data for methods testSetUploaderDirFalse, testSetDestDirFalse, testDirWithoutPermissions
+ *
+ * @return array
+ */
+ public function dataForUploaderDir()
+ {
+ return [
+ [
+ 'newSku' => [
+ 'downloadablesku1' => [
+ 'entity_id' => '25',
+ 'type_id' => 'downloadable',
+ 'attr_set_id' => '4',
+ 'attr_set_code' => 'Default',
+ ],
+ ],
+ 'bunch' => [
+ [
+ 'sku' => 'downloadablesku1',
+ 'product_type' => 'downloadable',
+ 'name' => 'Downloadable Product 1',
+ 'downloadable_samples' => 'group_title=Group Title Samples, title=Title 1, file=media/file.mp4'
+ .',sortorder=1|group_title=Group Title, title=Title 2, url=media/file2.mp4,sortorder=0',
+ 'downloadable_links' => 'group_title=Group Title Links, title=Title 1, price=10, downloads='
+ .'unlimited, file=media/file_link.mp4,sortorder=1|group_title=Group Title, title=Title 2,'
+ .' price=10, downloads=unlimited, url=media/file2.mp4,sortorder=0',
+ ],
+ ],
+ 'allowImport' => true,
+ ],
+ ];
+ }
+
+ /**
+ * Test for method prepareAttributesWithDefaultValueForSave
+ */
+ public function testPrepareAttributesWithDefaultValueForSave()
+ {
+ $rowData = [
+ '_attribute_set' => 'Default',
+ 'sku' => 'downloadablesku1',
+ 'product_type' => 'downloadable',
+ 'name' => 'Downloadable Product 1',
+ 'downloadable_samples' => 'group_title=Group Title Samples, title=Title 1, file=media/file.mp4,sortorder=1'
+ .'|group_title=Group Title, title=Title 2, url=media/file2.mp4,sortorder=0',
+ 'downloadable_links' => 'group_title=Group Title Links, title=Title 1, price=10, downloads=unlimited,'
+ .' file=media/file_link.mp4,sortorder=1|group_title=Group Title, title=Title 2, price=10, downloads'
+ .'=unlimited, url=media/file2.mp4,sortorder=0',
+ ];
+ $this->connectionMock->expects($this->any())->method('fetchAll')->with(
+ $this->select
+ )->willReturnOnConsecutiveCalls(
+ [
+ [
+ 'attribute_set_name' => '1',
+ 'attribute_id' => '1',
+ ],
+ [
+ 'attribute_set_name' => '2',
+ 'attribute_id' => '2',
+ ],
+ ]
+ );
+ $this->downloadableModelMock = $this->objectManagerHelper->getObject(
+ '\Magento\DownloadableImportExport\Model\Import\Product\Type\Downloadable',
+ [
+ 'attrSetColFac' => $this->attrSetColFacMock,
+ 'prodAttrColFac' => $this->prodAttrColFacMock,
+ 'resource' => $this->resourceMock,
+ 'params' => $this->paramsArray,
+ 'uploaderHelper' => $this->uploaderHelper,
+ 'downloadableHelper' => $this->downloadableHelper
+ ]
+ );
+ $this->setPropertyValue(
+ $this->downloadableModelMock,
+ '_attributes',
+ [
+ 'Default' => [
+ 'name' => [
+ 'id' => '69',
+ 'code' => 'name',
+ 'is_global' => '0',
+ 'is_required' => '1',
+ 'is_unique' => '0',
+ 'frontend_label' => 'Name',
+ 'is_static' => false,
+ 'apply_to' =>
+ [
+ ],
+ 'type' => 'varchar',
+ 'default_value' => null,
+ 'options' =>
+ [
+ ],
+ ],
+ 'sku' => [
+ 'id' => '70',
+ 'code' => 'sku',
+ 'is_global' => '1',
+ 'is_required' => '1',
+ 'is_unique' => '1',
+ 'frontend_label' => 'SKU',
+ 'is_static' => true,
+ 'apply_to' =>
+ [
+ ],
+ 'type' => 'varchar',
+ 'default_value' => null,
+ 'options' =>
+ [
+ ],
+ ]
+ ]
+ ]
+ );
+
+ $this->downloadableModelMock->prepareAttributesWithDefaultValueForSave($rowData);
+ }
+
+ /**
+ * @param $object
+ * @param $property
+ * @param $value
+ */
+ protected function setPropertyValue(&$object, $property, $value)
+ {
+ $reflection = new \ReflectionClass(get_class($object));
+ $reflectionProperty = $reflection->getProperty($property);
+ $reflectionProperty->setAccessible(true);
+ $reflectionProperty->setValue($object, $value);
+ return $object;
+ }
+}
diff --git a/app/code/Magento/DownloadableImportExport/composer.json b/app/code/Magento/DownloadableImportExport/composer.json
new file mode 100644
index 0000000000000..6d79b90effd59
--- /dev/null
+++ b/app/code/Magento/DownloadableImportExport/composer.json
@@ -0,0 +1,29 @@
+{
+ "name": "magento/module-downloadable-import-export",
+ "description": "N/A",
+ "require": {
+ "php": "~5.5.0|~5.6.0",
+ "magento/module-catalog": "1.0.0-beta",
+ "magento/module-import-export": "1.0.0-beta",
+ "magento/module-catalog-import-export": "1.0.0-beta",
+ "magento/module-downloadable": "1.0.0-beta",
+ "magento/module-store": "1.0.0-beta",
+ "magento/module-eav": "1.0.0-beta",
+ "magento/framework": "1.0.0-beta",
+ "magento/magento-composer-installer": "*"
+ },
+ "type": "magento2-module",
+ "version": "1.0.0-beta",
+ "license": [
+ "OSL-3.0",
+ "AFL-3.0"
+ ],
+ "extra": {
+ "map": [
+ [
+ "*",
+ "Magento/DownloadableImportExport"
+ ]
+ ]
+ }
+}
diff --git a/app/code/Magento/DownloadableImportExport/etc/import.xml b/app/code/Magento/DownloadableImportExport/etc/import.xml
new file mode 100644
index 0000000000000..fcce279dd795d
--- /dev/null
+++ b/app/code/Magento/DownloadableImportExport/etc/import.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/code/Magento/DownloadableImportExport/etc/module.xml b/app/code/Magento/DownloadableImportExport/etc/module.xml
new file mode 100644
index 0000000000000..83e3f829921dc
--- /dev/null
+++ b/app/code/Magento/DownloadableImportExport/etc/module.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/DownloadableImportExport/i18n/en_US.csv b/app/code/Magento/DownloadableImportExport/i18n/en_US.csv
new file mode 100644
index 0000000000000..e69de29bb2d1d
diff --git a/app/code/Magento/GroupedImportExport/Model/Export/RowCustomizer.php b/app/code/Magento/GroupedImportExport/Model/Export/RowCustomizer.php
index 0b565e2ac32c8..d47b50b8d1145 100644
--- a/app/code/Magento/GroupedImportExport/Model/Export/RowCustomizer.php
+++ b/app/code/Magento/GroupedImportExport/Model/Export/RowCustomizer.php
@@ -10,7 +10,12 @@
class RowCustomizer implements RowCustomizerInterface
{
/**
- * @inheritdoc
+ * Prepare data for export
+ *
+ * @param mixed $collection
+ * @param int $productIds
+ * @return mixed
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function prepareData($collection, $productIds)
{
@@ -18,23 +23,29 @@ public function prepareData($collection, $productIds)
}
/**
- * @inheritdoc
+ * Set headers columns
+ *
+ * @param array $columns
+ * @return mixed
*/
public function addHeaderColumns($columns)
{
$columns = array_merge(
$columns,
[
- '_associated_sku',
- '_associated_default_qty',
- '_associated_position'
+ 'associated_skus'
]
);
return $columns;
}
/**
- * @inheritdoc
+ * Add data for export
+ *
+ * @param array $dataRow
+ * @param int $productId
+ * @return mixed
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function addData($dataRow, $productId)
{
@@ -42,7 +53,12 @@ public function addData($dataRow, $productId)
}
/**
- * @inheritdoc
+ * Calculate the largest links block
+ *
+ * @param array $additionalRowsCount
+ * @param int $productId
+ * @return mixed
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function getAdditionalRowsCount($additionalRowsCount, $productId)
{
diff --git a/app/code/Magento/GroupedImportExport/Model/Import/Product/Type/Grouped.php b/app/code/Magento/GroupedImportExport/Model/Import/Product/Type/Grouped.php
index 5dc23fbd891a8..3df29a1d05596 100644
--- a/app/code/Magento/GroupedImportExport/Model/Import/Product/Type/Grouped.php
+++ b/app/code/Magento/GroupedImportExport/Model/Import/Product/Type/Grouped.php
@@ -69,6 +69,9 @@ public function saveData()
'relation' => []
];
foreach ($bunch as $rowNum => $rowData) {
+ if ($this->_type != $rowData[Product::COL_TYPE]) {
+ continue;
+ }
$associatedSkusQty = isset($rowData['associated_skus']) ? $rowData['associated_skus'] : null;
if (!$this->_entityModel->isRowAllowedToImport($rowData, $rowNum) || empty($associatedSkusQty)) {
continue;
@@ -96,9 +99,6 @@ public function saveData()
}
$productId = $productData['entity_id'];
- if ($this->_type != $rowData[Product::COL_TYPE]) {
- continue;
- }
$linksData['product_ids'][$productId] = true;
$linksData['relation'][] = ['parent_id' => $productId, 'child_id' => $linkedProductId];
$qty = empty($associatedSkuAndQty[1]) ? 0 : trim($associatedSkuAndQty[1]);
diff --git a/app/code/Magento/GroupedImportExport/Test/Unit/Model/Export/Product/RowCustomizerTest.php b/app/code/Magento/GroupedImportExport/Test/Unit/Model/Export/Product/RowCustomizerTest.php
new file mode 100644
index 0000000000000..c6d3d9e9d3dc8
--- /dev/null
+++ b/app/code/Magento/GroupedImportExport/Test/Unit/Model/Export/Product/RowCustomizerTest.php
@@ -0,0 +1,48 @@
+objectManagerHelper = new ObjectManagerHelper($this);
+ $this->rowCustomizerMock = $this->objectManagerHelper->getObject(
+ '\Magento\GroupedImportExport\Model\Export\RowCustomizer'
+ );
+ }
+
+ /**
+ * Test addHeaderColumns()
+ */
+ public function testAddHeaderColumns()
+ {
+ $productData = [0 => 'sku'];
+ $expectedData = [
+ 0 => 'sku',
+ 1 => 'associated_skus'
+ ];
+ $this->assertEquals($expectedData, $this->rowCustomizerMock->addHeaderColumns($productData));
+ }
+}
diff --git a/app/code/Magento/GroupedImportExport/Test/Unit/Model/Import/Product/Type/GroupedTest.php b/app/code/Magento/GroupedImportExport/Test/Unit/Model/Import/Product/Type/GroupedTest.php
index a2ea856ac2e2c..d6a6b3c40bb21 100644
--- a/app/code/Magento/GroupedImportExport/Test/Unit/Model/Import/Product/Type/GroupedTest.php
+++ b/app/code/Magento/GroupedImportExport/Test/Unit/Model/Import/Product/Type/GroupedTest.php
@@ -6,17 +6,13 @@
namespace Magento\GroupedImportExport\Test\Unit\Model\Import\Product\Type;
-use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper;
use \Magento\GroupedImportExport;
-class GroupedTest extends \PHPUnit_Framework_TestCase
+class GroupedTest extends \Magento\ImportExport\Test\Unit\Model\Import\AbstractImportTestCase
{
/** @var GroupedImportExport\Model\Import\Product\Type\Grouped */
protected $grouped;
- /** @var ObjectManagerHelper */
- protected $objectManagerHelper;
-
/**
* @var \Magento\Eav\Model\Resource\Entity\Attribute\Set\CollectionFactory|\PHPUnit_Framework_MockObject_MockObject
*/
@@ -64,6 +60,8 @@ class GroupedTest extends \PHPUnit_Framework_TestCase
protected function setUp()
{
+ parent::setUp();
+
$this->setCollectionFactory = $this->getMock(
'Magento\Eav\Model\Resource\Entity\Attribute\Set\CollectionFactory',
['create'],
@@ -93,11 +91,12 @@ protected function setUp()
$this->attrCollectionFactory->expects($this->any())->method('addFieldToFilter')->willReturn([]);
$this->entityModel = $this->getMock(
'Magento\CatalogImportExport\Model\Import\Product',
- ['getNewSku', 'getOldSku', 'getNextBunch', 'isRowAllowedToImport', 'getRowScope'],
+ ['getErrorAggregator', 'getNewSku', 'getOldSku', 'getNextBunch', 'isRowAllowedToImport', 'getRowScope'],
[],
'',
false
);
+ $this->entityModel->method('getErrorAggregator')->willReturn($this->getErrorAggregatorObject());
$this->params = [
0 => $this->entityModel,
1 => 'grouped'
@@ -109,12 +108,10 @@ protected function setUp()
'',
false
);
- $entityAttributes = [
- [
- 'attribute_id' => 'attribute_id',
- 'attribute_set_name' => 'attribute_set_name'
- ]
- ];
+ $entityAttributes = [[
+ 'attribute_set_name' => 'attribute_id',
+ 'attribute_id' => 'attributeSetName',
+ ]];
$this->connection = $this->getMock(
'Magento\Framework\DB\Adapter\Pdo\Mysql',
['select', 'fetchAll', 'fetchPairs', 'joinLeft', 'insertOnDuplicate', 'delete', 'quoteInto'],
@@ -149,7 +146,6 @@ protected function setUp()
);
$this->resource->expects($this->any())->method('getConnection')->will($this->returnValue($this->connection));
$this->resource->expects($this->any())->method('getTableName')->will($this->returnValue('tableName'));
- $this->objectManagerHelper = new ObjectManagerHelper($this);
$this->grouped = $this->objectManagerHelper->getObject(
'Magento\GroupedImportExport\Model\Import\Product\Type\Grouped',
[
diff --git a/app/code/Magento/ImportExport/Block/Adminhtml/Grid/Column/Renderer/Download.php b/app/code/Magento/ImportExport/Block/Adminhtml/Grid/Column/Renderer/Download.php
index 0057f0a73570b..0010eeba42e29 100644
--- a/app/code/Magento/ImportExport/Block/Adminhtml/Grid/Column/Renderer/Download.php
+++ b/app/code/Magento/ImportExport/Block/Adminhtml/Grid/Column/Renderer/Download.php
@@ -21,6 +21,8 @@ class Download extends \Magento\Backend\Block\Widget\Grid\Column\Renderer\Text
public function _getValue(\Magento\Framework\DataObject $row)
{
return ' ' . $row->getData('imported_file') . '
Download';
+ . $this->getUrl('*/*/download', ['filename' => $row->getData('imported_file')]) . '">'
+ . __('Download')
+ . '';
}
}
diff --git a/app/code/Magento/ImportExport/Block/Adminhtml/Grid/Column/Renderer/Error.php b/app/code/Magento/ImportExport/Block/Adminhtml/Grid/Column/Renderer/Error.php
new file mode 100644
index 0000000000000..def8e2d833dde
--- /dev/null
+++ b/app/code/Magento/ImportExport/Block/Adminhtml/Grid/Column/Renderer/Error.php
@@ -0,0 +1,32 @@
+getData('error_file') != '') {
+ $result = ' ' . $row->getData('error_file') . '
'
+ . __('Download')
+ . '';
+ }
+ return $result;
+ }
+}
diff --git a/app/code/Magento/ImportExport/Block/Adminhtml/Import/Edit/Before.php b/app/code/Magento/ImportExport/Block/Adminhtml/Import/Edit/Before.php
index a44a22e86384f..c72ca9d461051 100644
--- a/app/code/Magento/ImportExport/Block/Adminhtml/Import/Edit/Before.php
+++ b/app/code/Magento/ImportExport/Block/Adminhtml/Import/Edit/Before.php
@@ -56,6 +56,20 @@ public function getEntityBehaviors()
return $this->_jsonEncoder->encode($behaviors);
}
+ /**
+ * Returns json-encoded entity behaviors notes array
+ *
+ * @return string
+ */
+ public function getEntityBehaviorsNotes()
+ {
+ $behaviors = $this->_importModel->getEntityBehaviors();
+ foreach ($behaviors as $entityCode => $behavior) {
+ $behaviors[$entityCode] = $behavior['notes'];
+ }
+ return $this->_jsonEncoder->encode($behaviors);
+ }
+
/**
* Return json-encoded list of existing behaviors
*
diff --git a/app/code/Magento/ImportExport/Block/Adminhtml/Import/Edit/Form.php b/app/code/Magento/ImportExport/Block/Adminhtml/Import/Edit/Form.php
index 7ce277171063b..71f342b5b5857 100644
--- a/app/code/Magento/ImportExport/Block/Adminhtml/Import/Edit/Form.php
+++ b/app/code/Magento/ImportExport/Block/Adminhtml/Import/Edit/Form.php
@@ -5,6 +5,8 @@
*/
namespace Magento\ImportExport\Block\Adminhtml\Import\Edit;
+use Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingErrorAggregatorInterface;
+
/**
* Import edit form block
*
@@ -108,10 +110,45 @@ protected function _prepareForm()
'disabled' => true,
'values' => $this->_behaviorFactory->create($behaviorClass)->toOptionArray(),
'class' => $behaviorCode,
+ 'onchange' => 'varienImport.handleImportBehaviorSelector();',
+ 'note' => ' ',
+ ]
+ );
+ $fieldsets[$behaviorCode]->addField(
+ $behaviorCode . \Magento\ImportExport\Model\Import::FIELD_NAME_VALIDATION_STRATEGY,
+ 'select',
+ [
+ 'name' => \Magento\ImportExport\Model\Import::FIELD_NAME_VALIDATION_STRATEGY,
+ 'title' => __(' '),
+ 'label' => __(' '),
+ 'required' => true,
+ 'class' => $behaviorCode,
+ 'disabled' => true,
+ 'values' => [
+ ProcessingErrorAggregatorInterface::VALIDATION_STRATEGY_STOP_ON_ERROR => 'Stop on Error',
+ ProcessingErrorAggregatorInterface::VALIDATION_STRATEGY_SKIP_ERRORS => 'Skip error entries'
+ ],
+ 'after_element_html' => $this->getDownloadSampleFileHtml(),
+ ]
+ );
+ $fieldsets[$behaviorCode]->addField(
+ $behaviorCode . '_' . \Magento\ImportExport\Model\Import::FIELD_NAME_ALLOWED_ERROR_COUNT,
+ 'text',
+ [
+ 'name' => \Magento\ImportExport\Model\Import::FIELD_NAME_ALLOWED_ERROR_COUNT,
+ 'label' => __('Allowed Errors Count'),
+ 'title' => __('Allowed Errors Count'),
+ 'required' => true,
+ 'disabled' => true,
+ 'value' => 10,
+ 'class' => $behaviorCode . ' validate-number validate-greater-than-zero input-text',
+ 'note' => __(
+ 'Please specify number of errors to halt import process'
+ ),
]
);
$fieldsets[$behaviorCode]->addField(
- $behaviorCode . \Magento\ImportExport\Model\Import::FIELD_FIELD_SEPARATOR,
+ $behaviorCode . '_' . \Magento\ImportExport\Model\Import::FIELD_FIELD_SEPARATOR,
'text',
[
'name' => \Magento\ImportExport\Model\Import::FIELD_FIELD_SEPARATOR,
diff --git a/app/code/Magento/ImportExport/Controller/Adminhtml/Import.php b/app/code/Magento/ImportExport/Controller/Adminhtml/Import.php
index 319bef1a9cd42..0789bb1d04357 100644
--- a/app/code/Magento/ImportExport/Controller/Adminhtml/Import.php
+++ b/app/code/Magento/ImportExport/Controller/Adminhtml/Import.php
@@ -6,6 +6,8 @@
namespace Magento\ImportExport\Controller\Adminhtml;
use Magento\Backend\App\Action;
+use Magento\ImportExport\Model\Import\Entity\AbstractEntity;
+use Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingErrorAggregatorInterface;
/**
* Import controller
diff --git a/app/code/Magento/ImportExport/Controller/Adminhtml/Import/Start.php b/app/code/Magento/ImportExport/Controller/Adminhtml/Import/Start.php
index f8124344e9dfc..54c9049bcc91d 100644
--- a/app/code/Magento/ImportExport/Controller/Adminhtml/Import/Start.php
+++ b/app/code/Magento/ImportExport/Controller/Adminhtml/Import/Start.php
@@ -5,11 +5,34 @@
*/
namespace Magento\ImportExport\Controller\Adminhtml\Import;
-use Magento\ImportExport\Controller\Adminhtml\Import as ImportController;
+use Magento\ImportExport\Controller\Adminhtml\ImportResult as ImportResultController;
use Magento\Framework\Controller\ResultFactory;
-class Start extends ImportController
+class Start extends ImportResultController
{
+ /**
+ * @var \Magento\ImportExport\Model\Import
+ */
+ protected $importModel;
+
+ /**
+ * @param \Magento\Backend\App\Action\Context $context
+ * @param \Magento\ImportExport\Model\Report\ReportProcessorInterface $reportProcessor
+ * @param \Magento\ImportExport\Model\History $historyModel
+ * @param \Magento\ImportExport\Helper\Report $reportHelper
+ * @param \Magento\ImportExport\Model\Import $importModel
+ */
+ public function __construct(
+ \Magento\Backend\App\Action\Context $context,
+ \Magento\ImportExport\Model\Report\ReportProcessorInterface $reportProcessor,
+ \Magento\ImportExport\Model\History $historyModel,
+ \Magento\ImportExport\Helper\Report $reportHelper,
+ \Magento\ImportExport\Model\Import $importModel
+ ) {
+ parent::__construct($context, $reportProcessor, $historyModel, $reportHelper);
+ $this->importModel = $importModel;
+ }
+
/**
* Start import process action
*
@@ -23,23 +46,26 @@ public function execute()
$resultLayout = $this->resultFactory->create(ResultFactory::TYPE_LAYOUT);
/** @var $resultBlock \Magento\ImportExport\Block\Adminhtml\Import\Frame\Result */
$resultBlock = $resultLayout->getLayout()->getBlock('import.frame.result');
- /** @var $importModel \Magento\ImportExport\Model\Import */
- $importModel = $this->_objectManager->create('Magento\ImportExport\Model\Import');
+ $resultBlock
+ ->addAction('show', 'import_validation_container')
+ ->addAction('innerHTML', 'import_validation_container_header', __('Status'))
+ ->addAction('hide', ['edit_form', 'upload_button', 'messages']);
- try {
- $importModel->setData($data);
- $importModel->importSource();
- $importModel->invalidateIndex();
- $resultBlock->addAction('show', 'import_validation_container')
- ->addAction('innerHTML', 'import_validation_container_header', __('Status'));
- } catch (\Exception $e) {
- $resultBlock->addError($e->getMessage());
- return $resultLayout;
+ $this->importModel->setData($data);
+ $this->importModel->importSource();
+ $errorAggregator = $this->importModel->getErrorAggregator();
+ if ($this->importModel->getErrorAggregator()->hasToBeTerminated()) {
+ $resultBlock->addError(__('Maximum error count has been reached or system error is occurred!'));
+ $this->addErrorMessages($resultBlock, $errorAggregator);
+ } else {
+ $this->importModel->invalidateIndex();
+ $this->addErrorMessages($resultBlock, $errorAggregator);
+ $resultBlock->addSuccess(__('Import successfully done'));
}
- $resultBlock->addAction('hide', ['edit_form', 'upload_button', 'messages'])
- ->addSuccess(__('Import successfully done'));
+
return $resultLayout;
}
+
/** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */
$resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT);
$resultRedirect->setPath('adminhtml/*/index');
diff --git a/app/code/Magento/ImportExport/Controller/Adminhtml/Import/Validate.php b/app/code/Magento/ImportExport/Controller/Adminhtml/Import/Validate.php
index e09c555b4cce7..631966a9a40c2 100644
--- a/app/code/Magento/ImportExport/Controller/Adminhtml/Import/Validate.php
+++ b/app/code/Magento/ImportExport/Controller/Adminhtml/Import/Validate.php
@@ -5,57 +5,15 @@
*/
namespace Magento\ImportExport\Controller\Adminhtml\Import;
-use Magento\ImportExport\Controller\Adminhtml\Import as ImportController;
+use Magento\ImportExport\Controller\Adminhtml\ImportResult as ImportResultController;
use Magento\ImportExport\Model\Import;
use Magento\ImportExport\Block\Adminhtml\Import\Frame\Result as ImportResultBlock;
use Magento\Framework\Controller\ResultFactory;
use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\ImportExport\Model\Import\Adapter as ImportAdapter;
-class Validate extends ImportController
+class Validate extends ImportResultController
{
- /**
- * Process validation results
- *
- * @param \Magento\ImportExport\Model\Import $import
- * @param \Magento\ImportExport\Block\Adminhtml\Import\Frame\Result $resultBlock
- * @return void
- */
- protected function processValidationError(
- Import $import,
- ImportResultBlock $resultBlock
- ) {
- if ($import->getProcessedRowsCount() == $import->getInvalidRowsCount()) {
- $resultBlock->addNotice(__('This file is invalid. Please fix errors and re-upload the file.'));
- } elseif ($import->getErrorsCount() >= $import->getErrorsLimit()) {
- $resultBlock->addNotice(
- __(
- 'You\'ve reached an error limit (%1). Please fix errors and re-upload the file.',
- $import->getErrorsLimit()
- )
- );
- } else {
- if ($import->isImportAllowed()) {
- $resultBlock->addNotice(
- __(
- 'Please fix errors and re-upload the file. Or press "Import" to skip rows with errors.'
- ),
- true
- );
- } else {
- $resultBlock->addNotice(
- __('The file is partially valid, but we can\'t import it for some reason.'),
- false
- );
- }
- }
- // errors info
- foreach ($import->getErrors() as $errorCode => $rows) {
- $error = $errorCode . ' ' . __('in rows:') . ' ' . implode(', ', $rows);
- $resultBlock->addError($error);
- }
- }
-
/**
* Validate uploaded files action
*
@@ -66,7 +24,7 @@ public function execute()
$data = $this->getRequest()->getPostValue();
/** @var \Magento\Framework\View\Result\Layout $resultLayout */
$resultLayout = $this->resultFactory->create(ResultFactory::TYPE_LAYOUT);
- /** @var $resultBlock \Magento\ImportExport\Block\Adminhtml\Import\Frame\Result */
+ /** @var $resultBlock ImportResultBlock */
$resultBlock = $resultLayout->getLayout()->getBlock('import.frame.result');
if ($data) {
// common actions
@@ -75,49 +33,53 @@ public function execute()
'import_validation_container'
);
- try {
- /** @var $import \Magento\ImportExport\Model\Import */
- $import = $this->_objectManager->create('Magento\ImportExport\Model\Import')->setData($data);
- $source = ImportAdapter::findAdapterFor(
- $import->uploadSource(),
- $this->_objectManager->create('Magento\Framework\Filesystem')
- ->getDirectoryWrite(DirectoryList::ROOT),
- $data[$import::FIELD_FIELD_SEPARATOR]
- );
- $validationResult = $import->validateSource($source);
+ /** @var $import \Magento\ImportExport\Model\Import */
+ $import = $this->_objectManager->create('Magento\ImportExport\Model\Import')->setData($data);
+ $source = ImportAdapter::findAdapterFor(
+ $import->uploadSource(),
+ $this->_objectManager->create('Magento\Framework\Filesystem')
+ ->getDirectoryWrite(DirectoryList::ROOT),
+ $data[$import::FIELD_FIELD_SEPARATOR]
+ );
+ $validationResult = $import->validateSource($source);
- if (!$import->getProcessedRowsCount()) {
+ if (!$import->getProcessedRowsCount()) {
+ if (!$import->getErrorAggregator()->getErrorsCount()) {
$resultBlock->addError(__('This file is empty. Please try another one.'));
} else {
- if (!$validationResult) {
- $this->processValidationError($import, $resultBlock);
- } else {
- if ($import->isImportAllowed()) {
- $resultBlock->addSuccess(
- __('File is valid! To start import process press "Import" button'),
- true
- );
- } else {
- $resultBlock->addError(
- __('The file is valid, but we can\'t import it for some reason.'),
- false
- );
- }
+ foreach ($import->getErrorAggregator()->getAllErrors() as $error) {
+ $resultBlock->addError($error->getErrorMessage(), false);
}
- $resultBlock->addNotice($import->getNotices());
- $resultBlock->addNotice(
- __(
- 'Checked rows: %1, checked entities: %2, invalid rows: %3, total errors: %4',
- $import->getProcessedRowsCount(),
- $import->getProcessedEntitiesCount(),
- $import->getInvalidRowsCount(),
- $import->getErrorsCount()
- )
+ }
+ } else {
+ $errorAggregator = $import->getErrorAggregator();
+ if (!$validationResult) {
+ $resultBlock->addError(
+ __('Data validation is failed. Please fix errors and re-upload the file..')
);
+ $this->addErrorMessages($resultBlock, $errorAggregator);
+ } else {
+ if ($import->isImportAllowed()) {
+ $resultBlock->addSuccess(
+ __('File is valid! To start import process press "Import" button'),
+ true
+ );
+ } else {
+ $resultBlock->addError(
+ __('The file is valid, but we can\'t import it for some reason.'),
+ false
+ );
+ }
}
- } catch (\Exception $e) {
- $resultBlock->addNotice(__('Please fix errors and re-upload the file.'))
- ->addError($e->getMessage());
+ $resultBlock->addNotice(
+ __(
+ 'Checked rows: %1, checked entities: %2, invalid rows: %3, total errors: %4',
+ $import->getProcessedRowsCount(),
+ $import->getProcessedEntitiesCount(),
+ $errorAggregator->getInvalidRowsCount(),
+ $errorAggregator->getErrorsCount()
+ )
+ );
}
return $resultLayout;
} elseif ($this->getRequest()->isPost() && empty($_FILES)) {
diff --git a/app/code/Magento/ImportExport/Controller/Adminhtml/ImportResult.php b/app/code/Magento/ImportExport/Controller/Adminhtml/ImportResult.php
new file mode 100644
index 0000000000000..bfb3cdb95fd23
--- /dev/null
+++ b/app/code/Magento/ImportExport/Controller/Adminhtml/ImportResult.php
@@ -0,0 +1,151 @@
+reportProcessor = $reportProcessor;
+ $this->historyModel = $historyModel;
+ $this->reportHelper = $reportHelper;
+ }
+
+ /**
+ * @param \Magento\Framework\View\Element\AbstractBlock $resultBlock
+ * @param ProcessingErrorAggregatorInterface $errorAggregator
+ * @return $this
+ */
+ protected function addErrorMessages(
+ \Magento\Framework\View\Element\AbstractBlock $resultBlock,
+ ProcessingErrorAggregatorInterface $errorAggregator
+ ) {
+ if ($errorAggregator->getErrorsCount()) {
+ $message = '';
+ $counter = 0;
+ foreach ($this->getErrorMessages($errorAggregator) as $error) {
+ $message .= ++$counter . '. ' . $error . '
';
+ if ($counter >= self::LIMIT_ERRORS_MESSAGE) {
+ break;
+ }
+ }
+ if ($errorAggregator->hasFatalExceptions()) {
+ foreach ($this->getSystemExceptions($errorAggregator) as $error) {
+ $message .= $error->getErrorMessage()
+ . ' '
+ . __('Show more') . '' . __('Additional data') . ': '
+ . $error->getErrorDescription() . '
';
+ }
+ }
+ try {
+ $resultBlock->addNotice(
+ '' . __('Following Error(s) has been occurred during importing process:') . '
'
+ . ''
+ );
+ } catch (\Exception $e) {
+ foreach ($this->getErrorMessages($errorAggregator) as $errorMessage) {
+ $resultBlock->addError($errorMessage);
+ }
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * @param \Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingErrorAggregatorInterface $errorAggregator
+ * @return array
+ */
+ protected function getErrorMessages(ProcessingErrorAggregatorInterface $errorAggregator)
+ {
+ $messages = [];
+ $rowMessages = $errorAggregator->getRowsGroupedByErrorCode([], [AbstractEntity::ERROR_CODE_SYSTEM_EXCEPTION]);
+ foreach ($rowMessages as $errorCode => $rows) {
+ $messages[] = $errorCode . ' ' . __('in rows:') . ' ' . implode(', ', $rows);
+ }
+ return $messages;
+ }
+
+ /**
+ * @param ProcessingErrorAggregatorInterface $errorAggregator
+ * @return \Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingError[]
+ */
+ protected function getSystemExceptions(ProcessingErrorAggregatorInterface $errorAggregator)
+ {
+ return $errorAggregator->getErrorsByCode([AbstractEntity::ERROR_CODE_SYSTEM_EXCEPTION]);
+ }
+
+ /**
+ * @param ProcessingErrorAggregatorInterface $errorAggregator
+ * @return string
+ */
+ protected function createErrorReport(ProcessingErrorAggregatorInterface $errorAggregator)
+ {
+ $this->historyModel->loadLastInsertItem();
+ $sourceFile = $this->reportHelper->getReportAbsolutePath($this->historyModel->getImportedFile());
+ $writeOnlyErrorItems = true;
+ if ($this->historyModel->getData('execution_time') == ModelHistory::IMPORT_VALIDATION) {
+ $writeOnlyErrorItems = false;
+ }
+ $fileName = $this->reportProcessor->createReport($sourceFile, $errorAggregator, $writeOnlyErrorItems);
+ $this->historyModel->addErrorReportFile($fileName);
+ return $fileName;
+ }
+
+ /**
+ * @param string $fileName
+ * @return string
+ */
+ protected function createDownloadUrlImportHistoryFile($fileName)
+ {
+ return $this->getUrl(self::IMPORT_HISTORY_FILE_DOWNLOAD_ROUTE, ['filename' => $fileName]);
+ }
+}
diff --git a/app/code/Magento/ImportExport/Helper/Report.php b/app/code/Magento/ImportExport/Helper/Report.php
index 935a64fe13a5d..f05e0a790f26c 100644
--- a/app/code/Magento/ImportExport/Helper/Report.php
+++ b/app/code/Magento/ImportExport/Helper/Report.php
@@ -90,6 +90,15 @@ public function getReportOutput($filename)
return $this->varDirectory->readFile($this->getFilePath($filename));
}
+ /**
+ * @param string $fileName
+ * @return string
+ */
+ public function getReportAbsolutePath($fileName)
+ {
+ return $this->varDirectory->getAbsolutePath(Import::IMPORT_HISTORY_DIR . $fileName);
+ }
+
/**
* Retrieve report file size
*
diff --git a/app/code/Magento/ImportExport/Model/Export/AbstractEntity.php b/app/code/Magento/ImportExport/Model/Export/AbstractEntity.php
index e11145a53667d..d9780c68c1ab5 100644
--- a/app/code/Magento/ImportExport/Model/Export/AbstractEntity.php
+++ b/app/code/Magento/ImportExport/Model/Export/AbstractEntity.php
@@ -268,6 +268,20 @@ public function addMessageTemplate($errorCode, $message)
return $this;
}
+ /**
+ * Retrieve message template
+ *
+ * @param string $errorCode
+ * @return null|string
+ */
+ public function retrieveMessageTemplate($errorCode)
+ {
+ if (isset($this->_messageTemplates[$errorCode])) {
+ return $this->_messageTemplates[$errorCode];
+ }
+ return null;
+ }
+
/**
* Export process
*
diff --git a/app/code/Magento/ImportExport/Model/Export/Adapter/AbstractAdapter.php b/app/code/Magento/ImportExport/Model/Export/Adapter/AbstractAdapter.php
index 20a92c0f6c943..4e1087fb6aa9f 100644
--- a/app/code/Magento/ImportExport/Model/Export/Adapter/AbstractAdapter.php
+++ b/app/code/Magento/ImportExport/Model/Export/Adapter/AbstractAdapter.php
@@ -37,13 +37,17 @@ abstract class AbstractAdapter
/**
* Constructor
*
- * @param \Magento\Framework\Filesystem $filesystem
+ * @param Filesystem $filesystem
* @param string|null $destination
+ * @param string $destinationDirectoryCode
* @throws \Magento\Framework\Exception\LocalizedException
*/
- public function __construct(\Magento\Framework\Filesystem $filesystem, $destination = null)
- {
- $this->_directoryHandle = $filesystem->getDirectoryWrite(DirectoryList::SYS_TMP);
+ public function __construct(
+ \Magento\Framework\Filesystem $filesystem,
+ $destination = null,
+ $destinationDirectoryCode = DirectoryList::SYS_TMP
+ ) {
+ $this->_directoryHandle = $filesystem->getDirectoryWrite($destinationDirectoryCode);
if (!$destination) {
$destination = uniqid('importexport_');
$this->_directoryHandle->touch($destination);
diff --git a/app/code/Magento/ImportExport/Model/Export/Entity/AbstractEntity.php b/app/code/Magento/ImportExport/Model/Export/Entity/AbstractEntity.php
index 136ab28fb7c99..9ff3b8c8b45ec 100644
--- a/app/code/Magento/ImportExport/Model/Export/Entity/AbstractEntity.php
+++ b/app/code/Magento/ImportExport/Model/Export/Entity/AbstractEntity.php
@@ -346,6 +346,20 @@ public function addMessageTemplate($errorCode, $message)
return $this;
}
+ /**
+ * Retrieve message template
+ *
+ * @param string $errorCode
+ * @return null|string
+ */
+ public function retrieveMessageTemplate($errorCode)
+ {
+ if (isset($this->_messageTemplates[$errorCode])) {
+ return $this->_messageTemplates[$errorCode];
+ }
+ return null;
+ }
+
/**
* Export process.
*
diff --git a/app/code/Magento/ImportExport/Model/History.php b/app/code/Magento/ImportExport/Model/History.php
index 9cdad07dd5d3a..f21cf7edf8eed 100644
--- a/app/code/Magento/ImportExport/Model/History.php
+++ b/app/code/Magento/ImportExport/Model/History.php
@@ -23,6 +23,8 @@ class History extends \Magento\Framework\Model\AbstractModel
const IMPORTED_FILE = 'imported_file';
+ const ERROR_FILE = 'error_file';
+
const EXECUTION_TIME = 'execution_time';
const SUMMARY = 'summary';
@@ -91,6 +93,19 @@ public function addReport($filename)
return $this;
}
+ /**
+ * Add errors to import history report
+ *
+ * @param string $filename
+ * @return $this
+ */
+ public function addErrorReportFile($filename)
+ {
+ $this->setErrorFile($filename);
+ $this->save();
+ return $this;
+ }
+
/**
* Update import history report
*
@@ -170,6 +185,16 @@ public function getImportedFile()
return $this->getData(self::IMPORTED_FILE);
}
+ /**
+ * Get error file
+ *
+ * @return string
+ */
+ public function getErrorFile()
+ {
+ return $this->getData(self::ERROR_FILE);
+ }
+
/**
* Get import execution time
*
@@ -234,6 +259,16 @@ public function setImportedFile($importedFile)
return $this->setData(self::IMPORTED_FILE, $importedFile);
}
+ /**
+ * Set error file name
+ *
+ * @param string $errorFile
+ * @return $this
+ */
+ public function setErrorFile($errorFile)
+ {
+ return $this->setData(self::ERROR_FILE, $errorFile);
+ }
/**
* Set Execution Time
*
@@ -256,6 +291,16 @@ public function setSummary($summary)
return $this->setData(self::SUMMARY, $summary);
}
+ /**
+ * @return $this
+ */
+ public function loadLastInsertItem()
+ {
+ $this->load($this->getLastItemId());
+
+ return $this;
+ }
+
/**
* Retrieve admin ID
*
diff --git a/app/code/Magento/ImportExport/Model/Import.php b/app/code/Magento/ImportExport/Model/Import.php
index e46e82e6abe8d..94a7c1a6dd3c1 100644
--- a/app/code/Magento/ImportExport/Model/Import.php
+++ b/app/code/Magento/ImportExport/Model/Import.php
@@ -10,6 +10,8 @@
use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\Framework\HTTP\Adapter\FileTransferFactory;
+use Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingError;
+use Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingErrorAggregatorInterface;
/**
* Import model
@@ -56,6 +58,16 @@ class Import extends \Magento\ImportExport\Model\AbstractModel
*/
const FIELD_NAME_IMG_FILE_DIR = 'import_images_file_dir';
+ /**
+ * Allowed errors count field name
+ */
+ const FIELD_NAME_ALLOWED_ERROR_COUNT = 'allowed_error_count';
+
+ /**
+ * Validation startegt field name
+ */
+ const FIELD_NAME_VALIDATION_STRATEGY = 'validation_strategy';
+
/**
* Import field separator.
*/
@@ -199,9 +211,9 @@ public function __construct(
*/
protected function _getEntityAdapter()
{
+
if (!$this->_entityAdapter) {
$entities = $this->_importConfig->getEntities();
-
if (isset($entities[$this->getEntity()])) {
try {
$this->_entityAdapter = $this->_entityFactory->create($entities[$this->getEntity()]['model']);
@@ -255,31 +267,19 @@ protected function _getSourceAdapter($sourceFile)
/**
* Return operation result messages
*
- * @param bool $validationResult
+ * @param ProcessingErrorAggregatorInterface $validationResult
* @return string[]
*/
- public function getOperationResultMessages($validationResult)
+ public function getOperationResultMessages(ProcessingErrorAggregatorInterface $validationResult)
{
$messages = [];
if ($this->getProcessedRowsCount()) {
- if (!$validationResult) {
- if ($this->getProcessedRowsCount() == $this->getInvalidRowsCount()) {
- $messages[] = __('This file is invalid. Please fix errors and re-upload the file.');
- } elseif ($this->getErrorsCount() >= $this->getErrorsLimit()) {
- $messages[] = __(
- 'You\'ve reached an error limit (%1). Please fix errors and re-upload the file.',
- $this->getErrorsLimit()
- );
- } else {
- if ($this->isImportAllowed()) {
- $messages[] = __('Please fix errors and re-upload the file.');
- } else {
- $messages[] = __('The file is partially valid, but we can\'t import it for some reason.');
- }
- }
+ if ($validationResult->getErrorsCount()) {
+ $messages[] = __('Data validation is failed. Please fix errors and re-upload the file.');
+
// errors info
- foreach ($this->getErrors() as $errorCode => $rows) {
- $error = $errorCode . ' ' . __('in rows') . ': ' . implode(', ', $rows);
+ foreach ($validationResult->getRowsGroupedByErrorCode() as $errorMessage => $rows) {
+ $error = $errorMessage . ' ' . __('in rows') . ': ' . implode(', ', $rows);
$messages[] = $error;
}
} else {
@@ -289,16 +289,18 @@ public function getOperationResultMessages($validationResult)
$messages[] = __('The file is valid, but we can\'t import it for some reason.');
}
}
- $notices = $this->getNotices();
- if (is_array($notices)) {
- $messages = array_merge($messages, $notices);
- }
+
$messages[] = __(
'Checked rows: %1, checked entities: %2, invalid rows: %3, total errors: %4',
$this->getProcessedRowsCount(),
$this->getProcessedEntitiesCount(),
- $this->getInvalidRowsCount(),
- $this->getErrorsCount()
+ $validationResult->getInvalidRowsCount(),
+ $validationResult->getErrorsCount(
+ [
+ ProcessingError::ERROR_LEVEL_CRITICAL,
+ ProcessingError::ERROR_LEVEL_NOT_CRITICAL
+ ]
+ )
);
} else {
$messages[] = __('This file does not contain any data.');
@@ -358,56 +360,6 @@ public function getEntity()
return $this->_data['entity'];
}
- /**
- * Get entity adapter errors.
- *
- * @return array
- */
- public function getErrors()
- {
- return $this->_getEntityAdapter()->getErrorMessages();
- }
-
- /**
- * Returns error counter.
- *
- * @return int
- */
- public function getErrorsCount()
- {
- return $this->_getEntityAdapter()->getErrorsCount();
- }
-
- /**
- * Returns error limit value.
- *
- * @return int
- */
- public function getErrorsLimit()
- {
- return $this->_getEntityAdapter()->getErrorsLimit();
- }
-
- /**
- * Returns invalid rows count.
- *
- * @return int
- */
- public function getInvalidRowsCount()
- {
- return $this->_getEntityAdapter()->getInvalidRowsCount();
- }
-
- /**
- * Returns entity model noticees.
- *
- * @return string[]
- */
- public function getNotices()
- {
- return $this->_getEntityAdapter()->getNotices();
- }
-
/**
* Returns number of checked entities.
*
@@ -442,7 +394,6 @@ public function getWorkingDir()
* Import source file structure to DB.
*
* @return bool
- * @throws \Magento\Framework\Exception\AlreadyExistsException
* @throws \Magento\Framework\Exception\LocalizedException
*/
public function importSource()
@@ -453,32 +404,56 @@ public function importSource()
$this->addLogComment(__('Begin import of "%1" with "%2" behavior', $this->getEntity(), $this->getBehavior()));
- try {
- $result = $this->_getEntityAdapter()->importData();
- } catch (\Magento\Framework\Exception\AlreadyExistsException $e) {
- $this->importHistoryModel->invalidateReport($this);
- throw new \Magento\Framework\Exception\AlreadyExistsException(
- __($e->getMessage())
+ $result = $this->processImport();
+
+ if ($result) {
+ $this->addLogComment(
+ [
+ __(
+ 'Checked rows: %1, checked entities: %2, invalid rows: %3, total errors: %4',
+ $this->getProcessedRowsCount(),
+ $this->getProcessedEntitiesCount(),
+ $this->getErrorAggregator()->getInvalidRowsCount(),
+ $this->getErrorAggregator()->getErrorsCount()
+ ),
+ __('The import was successful.'),
+ ]
);
+ $this->importHistoryModel->updateReport($this, true);
+ } else {
+ $this->importHistoryModel->invalidateReport($this);
}
- $this->addLogComment(
- [
- __(
- 'Checked rows: %1, checked entities: %2, invalid rows: %3, total errors: %4',
- $this->getProcessedRowsCount(),
- $this->getProcessedEntitiesCount(),
- $this->getInvalidRowsCount(),
- $this->getErrorsCount()
- ),
- __('The import was successful.'),
- ]
- );
- $this->importHistoryModel->updateReport($this, true);
return $result;
}
+ /**
+ * @return bool
+ */
+ protected function processImport()
+ {
+ $errorAggregator = $this->_getEntityAdapter()->getErrorAggregator();
+ $errorAggregator->initValidationStrategy(
+ $this->getData(self::FIELD_NAME_VALIDATION_STRATEGY),
+ $this->getData(self::FIELD_NAME_ALLOWED_ERROR_COUNT)
+ );
+ try {
+ $this->_getEntityAdapter()->importData();
+ } catch (\Exception $e) {
+ $errorAggregator->addError(
+ \Magento\ImportExport\Model\Import\Entity\AbstractEntity::ERROR_CODE_SYSTEM_EXCEPTION,
+ ProcessingError::ERROR_LEVEL_CRITICAL,
+ null,
+ null,
+ null,
+ $e->getMessage()
+ );
+ }
+
+ return !$errorAggregator->hasToBeTerminated();
+ }
+
/**
* Import possibility getter.
*
@@ -489,6 +464,15 @@ public function isImportAllowed()
return $this->_getEntityAdapter()->isImportAllowed();
}
+ /**
+ * @return ProcessingErrorAggregatorInterface
+ * @throws \Magento\Framework\Exception\LocalizedException
+ */
+ public function getErrorAggregator()
+ {
+ return $this->_getEntityAdapter()->getErrorAggregator();
+ }
+
/**
* Move uploaded file and create source adapter instance.
*
@@ -577,11 +561,25 @@ protected function _removeBom($sourceFile)
public function validateSource(\Magento\ImportExport\Model\Import\AbstractSource $source)
{
$this->addLogComment(__('Begin data validation'));
- $adapter = $this->_getEntityAdapter()->setSource($source);
- $result = $adapter->isDataValid();
+ try {
+ $adapter = $this->_getEntityAdapter()->setSource($source);
+ $errorAggregator = $adapter->validateData();
+ } catch (\Exception $e) {
+ $errorAggregator = $this->getErrorAggregator();
+ $errorAggregator->addError(
+ \Magento\ImportExport\Model\Import\Entity\AbstractEntity::ERROR_CODE_SYSTEM_EXCEPTION,
+ ProcessingError::ERROR_LEVEL_CRITICAL,
+ null,
+ null,
+ null,
+ $e->getMessage()
+ );
+ }
- $messages = $this->getOperationResultMessages($result);
+ $messages = $this->getOperationResultMessages($errorAggregator);
$this->addLogComment($messages);
+
+ $result = !$errorAggregator->getErrorsCount();
if ($result) {
$this->addLogComment(__('Import data validation is complete.'));
}
@@ -636,6 +634,7 @@ public function getEntityBehaviors()
$behaviourData[$entityCode] = [
'token' => $behaviorClassName,
'code' => $behavior->getCode() . '_behavior',
+ 'notes' => $behavior->getNotes($entityCode),
];
} else {
throw new \Magento\Framework\Exception\LocalizedException(
@@ -678,12 +677,14 @@ public function getUniqueEntityBehaviors()
public function isReportEntityType($entity = null)
{
$result = false;
+ if (!$entity) {
+ $entity = $this->getEntity();
+ }
if ($entity !== null && $this->_getEntityAdapter()->getEntityTypeCode() != $entity) {
$entities = $this->_importConfig->getEntities();
- if (isset($entities[$this->getEntity()])) {
+ if (isset($entities[$entity])) {
try {
- $adapter = $this->_entityFactory->create($entities[$entity]['model']);
- $result = $adapter->isNeedToLogInHistory();
+ $result = $this->_getEntityAdapter()->isNeedToLogInHistory();
} catch (\Exception $e) {
throw new \Magento\Framework\Exception\LocalizedException(__('Please enter a correct entity model'));
}
diff --git a/app/code/Magento/ImportExport/Model/Import/AbstractEntity.php b/app/code/Magento/ImportExport/Model/Import/AbstractEntity.php
index f4a9e039a465f..50dc55f8ce762 100644
--- a/app/code/Magento/ImportExport/Model/Import/AbstractEntity.php
+++ b/app/code/Magento/ImportExport/Model/Import/AbstractEntity.php
@@ -5,6 +5,8 @@
*/
namespace Magento\ImportExport\Model\Import;
+use Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingError;
+use Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingErrorAggregatorInterface;
use Magento\Framework\App\Resource;
/**
@@ -40,6 +42,38 @@ abstract class AbstractEntity
const DB_MAX_TEXT_LENGTH = 65536;
+ const ERROR_CODE_SYSTEM_EXCEPTION = 'systemException';
+ const ERROR_CODE_COLUMN_NOT_FOUND = 'columnNotFound';
+ const ERROR_CODE_COLUMN_EMPTY_HEADER = 'columnEmptyHeader';
+ const ERROR_CODE_COLUMN_NAME_INVALID = 'columnNameInvalid';
+ const ERROR_CODE_ATTRIBUTE_NOT_VALID = 'attributeNotInvalid';
+ const ERROR_CODE_DUPLICATE_UNIQUE_ATTRIBUTE = 'duplicateUniqueAttribute';
+ const ERROR_CODE_ILLEGAL_CHARACTERS = 'illegalCharacters';
+ const ERROR_CODE_INVALID_ATTRIBUTE = 'invalidAttributeName';
+ const ERROR_CODE_WRONG_QUOTES = 'wrongQuotes';
+ const ERROR_CODE_COLUMNS_NUMBER = 'wrongColumnsNumber';
+ const ERROR_EXCEEDED_MAX_LENGTH = 'exceededMaxLength';
+ const ERROR_INVALID_ATTRIBUTE_TYPE = 'invalidAttributeType';
+ const ERROR_INVALID_ATTRIBUTE_OPTION = 'absentAttributeOption';
+
+ protected $errorMessageTemplates = [
+ self::ERROR_CODE_SYSTEM_EXCEPTION => 'General system exception happened',
+ self::ERROR_CODE_COLUMN_NOT_FOUND => 'We can\'t find required columns: %s.',
+ self::ERROR_CODE_COLUMN_EMPTY_HEADER => 'Columns number: "%s" have empty headers',
+ self::ERROR_CODE_COLUMN_NAME_INVALID => 'Column names: "%s" are invalid',
+ self::ERROR_CODE_ATTRIBUTE_NOT_VALID => "Please correct the value for '%s'",
+ self::ERROR_CODE_DUPLICATE_UNIQUE_ATTRIBUTE => "Duplicate Unique Attribute for '%s'",
+ self::ERROR_CODE_ILLEGAL_CHARACTERS => "Illegal character used for attribute %s",
+ self::ERROR_CODE_INVALID_ATTRIBUTE => 'Header contains invalid attribute(s): "%s"',
+ self::ERROR_CODE_WRONG_QUOTES => "Curly quotes used instead of straight quotes",
+ self::ERROR_CODE_COLUMNS_NUMBER => "Number of columns does not correspond to the number of rows in the header",
+ self::ERROR_EXCEEDED_MAX_LENGTH => 'Attribute %s exceeded max length',
+ self::ERROR_INVALID_ATTRIBUTE_TYPE =>
+ 'Value for \'%s\' attribute contains incorrect value, acceptable values are in %s format',
+ self::ERROR_INVALID_ATTRIBUTE_OPTION =>
+ "Value for %s attribute contains incorrect value, see acceptable values on settings specified for Admin",
+ ];
+
/**#@-*/
/**
@@ -57,32 +91,30 @@ abstract class AbstractEntity
protected $_dataValidated = false;
/**
- * DB data source model
+ * Valid column names
*
- * @var \Magento\ImportExport\Model\Resource\Import\Data
+ * @array
*/
- protected $_dataSourceModel;
+ protected $validColumnNames = [];
/**
- * Error codes with arrays of corresponding row numbers
+ * If we should check column names
*
- * @var array
+ * @var bool
*/
- protected $_errors = [];
+ protected $needColumnCheck = false;
/**
- * Error counter
+ * DB data source model
*
- * @var int
+ * @var \Magento\ImportExport\Model\Resource\Import\Data
*/
- protected $_errorsCount = 0;
+ protected $_dataSourceModel;
/**
- * Limit of errors after which pre-processing will exit
- *
- * @var int
+ * @var ProcessingErrorAggregatorInterface
*/
- protected $_errorsLimit = 100;
+ protected $errorAggregator;
/**
* Flag to disable import
@@ -91,27 +123,6 @@ abstract class AbstractEntity
*/
protected $_importAllowed = true;
- /**
- * Array of invalid rows numbers
- *
- * @var array
- */
- protected $_invalidRows = [];
-
- /**
- * Validation failure message template definitions
- *
- * @var array
- */
- protected $_messageTemplates = [];
-
- /**
- * Notice messages
- *
- * @var string[]
- */
- protected $_notices = [];
-
/**
* Magento string lib
*
@@ -159,7 +170,7 @@ abstract class AbstractEntity
*
* @var bool
*/
- protected $logInHistory = false;
+ protected $logInHistory = true;
/**
* Rows which will be skipped during import
@@ -239,12 +250,34 @@ abstract class AbstractEntity
*/
protected $_scopeConfig;
+ /**
+ * Count if created items
+ *
+ * @var int
+ */
+ protected $countItemsCreated = 0;
+
+ /**
+ * Count if updated items
+ *
+ * @var int
+ */
+ protected $countItemsUpdated = 0;
+
+ /**
+ * Count if deleted items
+ *
+ * @var int
+ */
+ protected $countItemsDeleted = 0;
+
/**
* @param \Magento\Framework\Stdlib\StringUtils $string
* @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
* @param \Magento\ImportExport\Model\ImportFactory $importFactory
* @param \Magento\ImportExport\Model\Resource\Helper $resourceHelper
- * @param Resource $resource
+ * @param \Magento\Framework\App\Resource $resource
+ * @param ProcessingErrorAggregatorInterface $errorAggregator
* @param array $data
* @SuppressWarnings(PHPMD.NPathComplexity)
*/
@@ -254,6 +287,7 @@ public function __construct(
\Magento\ImportExport\Model\ImportFactory $importFactory,
\Magento\ImportExport\Model\Resource\Helper $resourceHelper,
Resource $resource,
+ ProcessingErrorAggregatorInterface $errorAggregator,
array $data = []
) {
$this->_scopeConfig = $scopeConfig;
@@ -280,6 +314,20 @@ public function __construct(
static::XML_PATH_BUNCH_SIZE,
\Magento\Store\Model\ScopeInterface::SCOPE_STORE
) : 0);
+
+ $this->errorAggregator = $errorAggregator;
+
+ foreach ($this->errorMessageTemplates as $errorCode => $message) {
+ $this->getErrorAggregator()->addErrorMessageTemplate($errorCode, $message);
+ }
+ }
+
+ /**
+ * @return ProcessingErrorAggregatorInterface
+ */
+ public function getErrorAggregator()
+ {
+ return $this->errorAggregator;
}
/**
@@ -319,6 +367,25 @@ protected function _prepareRowForDb(array $rowData)
return $rowData;
}
+ /**
+ * Add errors to error aggregator
+ *
+ * @param string $code
+ * @param array|mixed $errors
+ * @return void
+ */
+ protected function addErrors($code, $errors)
+ {
+ if ($errors) {
+ $this->getErrorAggregator()->addError(
+ $code,
+ ProcessingError::ERROR_LEVEL_CRITICAL,
+ null,
+ implode('", "', $errors)
+ );
+ }
+ }
+
/**
* Validate data rows and save bunches to DB
*
@@ -351,11 +418,28 @@ protected function _saveValidatedBunches()
$startNewBunch = false;
}
if ($source->valid()) {
- // errors limit check
- if ($this->_errorsCount >= $this->_errorsLimit) {
- return $this;
+ $valid = true;
+ try {
+ $rowData = $source->current();
+ foreach ($rowData as $attrName => $element) {
+ if (!mb_check_encoding($element, 'UTF-8')) {
+ $valid = false;
+ $this->addRowError(
+ AbstractEntity::ERROR_CODE_ILLEGAL_CHARACTERS,
+ $this->_processedRowsCount,
+ $attrName
+ );
+ }
+ }
+ } catch (\InvalidArgumentException $e) {
+ $valid = false;
+ $this->addRowError($e->getMessage(), $this->_processedRowsCount);
+ }
+ if (!$valid) {
+ $this->_processedRowsCount++;
+ $source->next();
+ continue;
}
- $rowData = $source->current();
if (isset($rowData[$masterAttributeCode]) && trim($rowData[$masterAttributeCode])) {
/* Add entity group that passed validation to bunch */
@@ -390,20 +474,33 @@ protected function _saveValidatedBunches()
}
/**
- * Add error with corresponding current data source row number
+ * Add error with corresponding current data source row number.
*
* @param string $errorCode Error code or simply column name
- * @param int $errorRowNum Row number
- * @param string $columnName OPTIONAL Column name
+ * @param int $errorRowNum Row number.
+ * @param string $colName OPTIONAL Column name.
+ * @param string $errorMessage OPTIONAL Column name.
+ * @param string $errorLevel
+ * @param string $errorDescription
* @return $this
*/
- public function addRowError($errorCode, $errorRowNum, $columnName = null)
- {
+ public function addRowError(
+ $errorCode,
+ $errorRowNum,
+ $colName = null,
+ $errorMessage = null,
+ $errorLevel = ProcessingError::ERROR_LEVEL_CRITICAL,
+ $errorDescription = null
+ ) {
$errorCode = (string)$errorCode;
- $this->_errors[$errorCode][] = [$errorRowNum + 1, $columnName];
- // one added for human readability
- $this->_invalidRows[$errorRowNum] = true;
- $this->_errorsCount++;
+ $this->getErrorAggregator()->addError(
+ $errorCode,
+ $errorLevel,
+ $errorRowNum,
+ $colName,
+ $errorMessage,
+ $errorDescription
+ );
return $this;
}
@@ -417,7 +514,7 @@ public function addRowError($errorCode, $errorRowNum, $columnName = null)
*/
public function addMessageTemplate($errorCode, $message)
{
- $this->_messageTemplates[$errorCode] = $message;
+ $this->getErrorAggregator()->addErrorMessageTemplate($errorCode, $message);
return $this;
}
@@ -471,66 +568,6 @@ public static function getDefaultBehavior()
return \Magento\ImportExport\Model\Import::BEHAVIOR_ADD_UPDATE;
}
- /**
- * Returns error information grouped by error types and translated (if possible)
- *
- * @return array
- */
- public function getErrorMessages()
- {
- $messages = [];
- foreach ($this->_errors as $errorCode => $errorRows) {
- if (isset($this->_messageTemplates[$errorCode])) {
- $errorCode = (string)__($this->_messageTemplates[$errorCode]);
- }
- foreach ($errorRows as $errorRowData) {
- $key = $errorRowData[1] ? sprintf($errorCode, $errorRowData[1]) : $errorCode;
- $messages[$key][] = $errorRowData[0];
- }
- }
- return $messages;
- }
-
- /**
- * Returns error counter value
- *
- * @return int
- */
- public function getErrorsCount()
- {
- return $this->_errorsCount;
- }
-
- /**
- * Returns error limit value
- *
- * @return int
- */
- public function getErrorsLimit()
- {
- return $this->_errorsLimit;
- }
-
- /**
- * Returns invalid rows count
- *
- * @return int
- */
- public function getInvalidRowsCount()
- {
- return count($this->_invalidRows);
- }
-
- /**
- * Returns model notices
- *
- * @return string[]
- */
- public function getNotices()
- {
- return $this->_notices;
- }
-
/**
* Returns number of checked entities
*
@@ -606,30 +643,37 @@ public function getMasterAttributeCode()
*/
public function isAttributeValid($attributeCode, array $attributeParams, array $rowData, $rowNumber)
{
+ $message = '';
switch ($attributeParams['type']) {
case 'varchar':
$value = $this->string->cleanString($rowData[$attributeCode]);
$valid = $this->string->strlen($value) < self::DB_MAX_VARCHAR_LENGTH;
+ $message = self::ERROR_EXCEEDED_MAX_LENGTH;
break;
case 'decimal':
$value = trim($rowData[$attributeCode]);
$valid = (double)$value == $value && is_numeric($value);
+ $message = self::ERROR_INVALID_ATTRIBUTE_TYPE;
break;
case 'select':
case 'multiselect':
$valid = isset($attributeParams['options'][strtolower($rowData[$attributeCode])]);
+ $message = self::ERROR_INVALID_ATTRIBUTE_OPTION;
break;
case 'int':
$value = trim($rowData[$attributeCode]);
$valid = (int)$value == $value && is_numeric($value);
+ $message = self::ERROR_INVALID_ATTRIBUTE_TYPE;
break;
case 'datetime':
$value = trim($rowData[$attributeCode]);
$valid = strtotime($value) !== false;
+ $message = self::ERROR_INVALID_ATTRIBUTE_TYPE;
break;
case 'text':
$value = $this->string->cleanString($rowData[$attributeCode]);
$valid = $this->string->strlen($value) < self::DB_MAX_TEXT_LENGTH;
+ $message = self::ERROR_EXCEEDED_MAX_LENGTH;
break;
default:
$valid = true;
@@ -637,10 +681,17 @@ public function isAttributeValid($attributeCode, array $attributeParams, array $
}
if (!$valid) {
- $this->addRowError(__("Please correct the value for '%s'."), $rowNumber, $attributeCode);
+ if ($message == self::ERROR_INVALID_ATTRIBUTE_TYPE) {
+ $message = sprintf(
+ $this->errorMessageTemplates[$message],
+ $attributeCode,
+ $attributeParams['type']
+ );
+ }
+ $this->addRowError($message, $rowNumber, $attributeCode);
} elseif (!empty($attributeParams['is_unique'])) {
if (isset($this->_uniqueAttributes[$attributeCode][$rowData[$attributeCode]])) {
- $this->addRowError(__("Duplicate Unique Attribute for '%s'"), $rowNumber, $attributeCode);
+ $this->addRowError(self::ERROR_CODE_DUPLICATE_UNIQUE_ATTRIBUTE, $rowNumber, $attributeCode);
return false;
}
$this->_uniqueAttributes[$attributeCode][$rowData[$attributeCode]] = true;
@@ -648,17 +699,6 @@ public function isAttributeValid($attributeCode, array $attributeParams, array $
return (bool)$valid;
}
- /**
- * Check that is all of data valid
- *
- * @return bool
- */
- public function isDataValid()
- {
- $this->validateData();
- return 0 == $this->getErrorsCount();
- }
-
/**
* Import possibility getter
*
@@ -729,25 +769,22 @@ public function setSource(AbstractSource $source)
/**
* Validate data
*
- * @return $this
+ * @return ProcessingErrorAggregatorInterface
* @throws \Magento\Framework\Exception\LocalizedException
- * @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
public function validateData()
{
if (!$this->_dataValidated) {
+ $this->getErrorAggregator()->clear();
// do all permanent columns exist?
$absentColumns = array_diff($this->_permanentAttributes, $this->getSource()->getColNames());
- if ($absentColumns) {
- throw new \Magento\Framework\Exception\LocalizedException(
- __('We can\'t find required columns: %1.', implode(', ', $absentColumns))
- );
- }
+ $this->addErrors(self::ERROR_CODE_COLUMN_NOT_FOUND, $absentColumns);
// check attribute columns names validity
$columnNumber = 0;
$emptyHeaderColumns = [];
$invalidColumns = [];
+ $invalidAttributes = [];
foreach ($this->getSource()->getColNames() as $columnName) {
$columnNumber++;
if (!$this->isAttributeParticular($columnName)) {
@@ -755,27 +792,66 @@ public function validateData()
$emptyHeaderColumns[] = $columnNumber;
} elseif (!preg_match('/^[a-z][a-z0-9_]*$/', $columnName)) {
$invalidColumns[] = $columnName;
+ } elseif ($this->needColumnCheck && !in_array($columnName, $this->validColumnNames)) {
+ $invalidAttributes[] = $columnName;
}
}
}
+ $this->addErrors(self::ERROR_CODE_INVALID_ATTRIBUTE, $invalidAttributes);
+ $this->addErrors(self::ERROR_CODE_COLUMN_EMPTY_HEADER, $emptyHeaderColumns);
+ $this->addErrors(self::ERROR_CODE_COLUMN_NAME_INVALID, $invalidColumns);
- if ($emptyHeaderColumns) {
- throw new \Magento\Framework\Exception\LocalizedException(
- __('Columns number: "%1" have empty headers', implode('", "', $emptyHeaderColumns))
- );
+ if (!$this->getErrorAggregator()->getErrorsCount()) {
+ $this->_saveValidatedBunches();
+ $this->_dataValidated = true;
}
- if ($invalidColumns) {
- throw new \Magento\Framework\Exception\LocalizedException(
- __('Column names: "%1" are invalid', implode('", "', $invalidColumns))
- );
- }
-
- // initialize validation related attributes
- $this->_errors = [];
- $this->_invalidRows = [];
- $this->_saveValidatedBunches();
- $this->_dataValidated = true;
}
+ return $this->getErrorAggregator();
+ }
+
+ /**
+ * Get count of created items
+ *
+ * @return int
+ */
+ public function getCreatedItemsCount()
+ {
+ return $this->countItemsCreated;
+ }
+
+ /**
+ * Get count of updated items
+ *
+ * @return int
+ */
+ public function getUpdatedItemsCount()
+ {
+ return $this->countItemsUpdated;
+ }
+
+ /**
+ * Get count of deleted items
+ *
+ * @return int
+ */
+ public function getDeletedItemsCount()
+ {
+ return $this->countItemsDeleted;
+ }
+
+ /**
+ * Update proceed items counter
+ *
+ * @param array $created
+ * @param array $updated
+ * @param array $deleted
+ * @return $this
+ */
+ protected function updateItemsCounterStats(array $created = [], array $updated = [], array $deleted = [])
+ {
+ $this->countItemsCreated = count($created);
+ $this->countItemsUpdated = count($updated);
+ $this->countItemsDeleted = count($deleted);
return $this;
}
}
diff --git a/app/code/Magento/ImportExport/Model/Import/AbstractSource.php b/app/code/Magento/ImportExport/Model/Import/AbstractSource.php
index 05922175aa09a..d91b394a5c080 100644
--- a/app/code/Magento/ImportExport/Model/Import/AbstractSource.php
+++ b/app/code/Magento/ImportExport/Model/Import/AbstractSource.php
@@ -5,6 +5,8 @@
*/
namespace Magento\ImportExport\Model\Import;
+use Magento\ImportExport\Model\Import\AbstractEntity;
+
/**
* Data source with columns for Magento_ImportExport
*/
@@ -38,6 +40,11 @@ abstract class AbstractSource implements \SeekableIterator
*/
protected $_key = -1;
+ /**
+ * @var bool
+ */
+ protected $_foundWrongQuoteFlag = false;
+
/**
* Get and validate column names
*
@@ -77,7 +84,11 @@ public function current()
{
$row = $this->_row;
if (count($row) != $this->_colQty) {
- $row = array_pad($this->_row, $this->_colQty, '');
+ if ($this->_foundWrongQuoteFlag) {
+ throw new \InvalidArgumentException(AbstractEntity::ERROR_CODE_WRONG_QUOTES);
+ } else {
+ throw new \InvalidArgumentException(AbstractEntity::ERROR_CODE_COLUMNS_NUMBER);
+ }
}
return array_combine($this->_colNames, $row);
}
diff --git a/app/code/Magento/ImportExport/Model/Import/Entity/AbstractEav.php b/app/code/Magento/ImportExport/Model/Import/Entity/AbstractEav.php
index 38d488a52e752..2e9d08c3e4178 100644
--- a/app/code/Magento/ImportExport/Model/Import/Entity/AbstractEav.php
+++ b/app/code/Magento/ImportExport/Model/Import/Entity/AbstractEav.php
@@ -5,6 +5,8 @@
*/
namespace Magento\ImportExport\Model\Import\Entity;
+use Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingErrorAggregatorInterface;
+
/**
* Import EAV entity abstract model
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
@@ -78,6 +80,7 @@ abstract class AbstractEav extends \Magento\ImportExport\Model\Import\AbstractEn
* @param \Magento\ImportExport\Model\ImportFactory $importFactory
* @param \Magento\ImportExport\Model\Resource\Helper $resourceHelper
* @param \Magento\Framework\App\Resource $resource
+ * @param ProcessingErrorAggregatorInterface $errorAggregator
* @param \Magento\Store\Model\StoreManagerInterface $storeManager
* @param \Magento\ImportExport\Model\Export\Factory $collectionFactory
* @param \Magento\Eav\Model\Config $eavConfig
@@ -91,12 +94,13 @@ public function __construct(
\Magento\ImportExport\Model\ImportFactory $importFactory,
\Magento\ImportExport\Model\Resource\Helper $resourceHelper,
\Magento\Framework\App\Resource $resource,
+ ProcessingErrorAggregatorInterface $errorAggregator,
\Magento\Store\Model\StoreManagerInterface $storeManager,
\Magento\ImportExport\Model\Export\Factory $collectionFactory,
\Magento\Eav\Model\Config $eavConfig,
array $data = []
) {
- parent::__construct($string, $scopeConfig, $importFactory, $resourceHelper, $resource, $data);
+ parent::__construct($string, $scopeConfig, $importFactory, $resourceHelper, $resource, $errorAggregator, $data);
$this->_storeManager = $storeManager;
$this->_attributeCollection = isset(
@@ -176,6 +180,7 @@ protected function _initAttributes()
'type' => \Magento\ImportExport\Model\Import::getAttributeType($attribute),
'options' => $this->getAttributeOptions($attribute),
];
+ $this->validColumnNames[] = $attribute->getAttributeCode();
}
return $this;
}
diff --git a/app/code/Magento/ImportExport/Model/Import/Entity/AbstractEntity.php b/app/code/Magento/ImportExport/Model/Import/Entity/AbstractEntity.php
index 078e81c60aaa2..79c12cd16a7c4 100644
--- a/app/code/Magento/ImportExport/Model/Import/Entity/AbstractEntity.php
+++ b/app/code/Magento/ImportExport/Model/Import/Entity/AbstractEntity.php
@@ -8,6 +8,8 @@
use Magento\Framework\App\Resource;
use Magento\ImportExport\Model\Import\AbstractSource;
use Magento\ImportExport\Model\Import as ImportExport;
+use Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingError;
+use Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingErrorAggregatorInterface;
/**
* Import entity abstract model
@@ -27,6 +29,37 @@ abstract class AbstractEntity
const DB_MAX_TEXT_LENGTH = 65536;
+ const ERROR_CODE_SYSTEM_EXCEPTION = 'systemException';
+ const ERROR_CODE_COLUMN_NOT_FOUND = 'columnNotFound';
+ const ERROR_CODE_COLUMN_EMPTY_HEADER = 'columnEmptyHeader';
+ const ERROR_CODE_COLUMN_NAME_INVALID = 'columnNameInvalid';
+ const ERROR_CODE_ATTRIBUTE_NOT_VALID = 'attributeNotInvalid';
+ const ERROR_CODE_DUPLICATE_UNIQUE_ATTRIBUTE = 'duplicateUniqueAttribute';
+ const ERROR_CODE_ILLEGAL_CHARACTERS = 'illegalCharacters';
+ const ERROR_CODE_INVALID_ATTRIBUTE = 'invalidAttributeName';
+ const ERROR_CODE_WRONG_QUOTES = 'wrongQuotes';
+ const ERROR_CODE_COLUMNS_NUMBER = 'wrongColumnsNumber';
+
+ protected $errorMessageTemplates = [
+ self::ERROR_CODE_SYSTEM_EXCEPTION => 'General system exception happened',
+ self::ERROR_CODE_COLUMN_NOT_FOUND => 'We can\'t find required columns: %s.',
+ self::ERROR_CODE_COLUMN_EMPTY_HEADER => 'Columns number: "%s" have empty headers',
+ self::ERROR_CODE_COLUMN_NAME_INVALID => 'Column names: "%s" are invalid',
+ self::ERROR_CODE_ATTRIBUTE_NOT_VALID => "Please correct the value for '%s'.",
+ self::ERROR_CODE_DUPLICATE_UNIQUE_ATTRIBUTE => "Duplicate Unique Attribute for '%s'",
+ self::ERROR_CODE_ILLEGAL_CHARACTERS => "Illegal character used for attribute %s",
+ self::ERROR_CODE_INVALID_ATTRIBUTE => 'Header contains invalid attribute(s): "%s"',
+ self::ERROR_CODE_WRONG_QUOTES => "Curly quotes used instead of straight quotes",
+ self::ERROR_CODE_COLUMNS_NUMBER => "Number of columns does not correspond to the number of rows in the header",
+ ];
+
+ /**
+ * Validation failure message template definitions
+ *
+ * @var array
+ */
+ protected $_messageTemplates = [];
+
/**
* DB connection.
*
@@ -35,46 +68,46 @@ abstract class AbstractEntity
protected $_connection;
/**
- * Has data process validation done?
+ * Has data process validation done?8
*
* @var bool
*/
protected $_dataValidated = false;
/**
- * DB data source model.
+ * Valid column names
*
- * @var \Magento\ImportExport\Model\Resource\Import\Data
+ * @array
*/
- protected $_dataSourceModel;
+ protected $validColumnNames = [];
/**
- * Entity type id.
+ * If we should check column names
*
- * @var int
+ * @var bool
*/
- protected $_entityTypeId;
+ protected $needColumnCheck = false;
/**
- * Error codes with arrays of corresponding row numbers.
+ * DB data source model.
*
- * @var array
+ * @var \Magento\ImportExport\Model\Resource\Import\Data
*/
- protected $_errors = [];
+ protected $_dataSourceModel;
/**
- * Error counter.
+ * Entity type id.
*
* @var int
*/
- protected $_errorsCount = 0;
+ protected $_entityTypeId;
/**
- * Limit of errors after which pre-processing will exit.
+ * Error codes with arrays of corresponding row numbers.
*
- * @var int
+ * @var array
*/
- protected $_errorsLimit = 100;
+ protected $_errors = [];
/**
* Flag to disable import.
@@ -90,27 +123,6 @@ abstract class AbstractEntity
*/
protected $_indexValueAttributes = [];
- /**
- * Array of invalid rows numbers.
- *
- * @var array
- */
- protected $_invalidRows = [];
-
- /**
- * Validation failure message template definitions.
- *
- * @var array
- */
- protected $_messageTemplates = [];
-
- /**
- * Notice messages.
- *
- * @var string[]
- */
- protected $_notices = [];
-
/**
* Entity model parameters.
*
@@ -146,17 +158,6 @@ abstract class AbstractEntity
*/
protected $_processedRowsCount = 0;
- /**
- * Rows to skip. Valid rows but we have some reasons to skip them.
- *
- * [Row number 1] => true,
- * ...
- * [Row number N] => true
- *
- * @var array
- */
- protected $_rowsToSkip = [];
-
/**
* Array of numbers of validated rows as keys and boolean TRUE as values.
*
@@ -232,6 +233,11 @@ abstract class AbstractEntity
*/
protected $logInHistory = false;
+ /**
+ * @var ProcessingErrorAggregatorInterface
+ */
+ protected $errorAggregator;
+
/**
* @param \Magento\Framework\Json\Helper\Data $jsonHelper
* @param \Magento\ImportExport\Helper\Data $importExportData
@@ -240,6 +246,8 @@ abstract class AbstractEntity
* @param Resource $resource
* @param \Magento\ImportExport\Model\Resource\Helper $resourceHelper
* @param \Magento\Framework\Stdlib\StringUtils $string
+ * @param ProcessingErrorAggregatorInterface $errorAggregator
+ * @throws \Magento\Framework\Exception\LocalizedException
*/
public function __construct(
\Magento\Framework\Json\Helper\Data $jsonHelper,
@@ -248,12 +256,18 @@ public function __construct(
\Magento\Eav\Model\Config $config,
Resource $resource,
\Magento\ImportExport\Model\Resource\Helper $resourceHelper,
- \Magento\Framework\Stdlib\StringUtils $string
+ \Magento\Framework\Stdlib\StringUtils $string,
+ ProcessingErrorAggregatorInterface $errorAggregator
) {
$this->jsonHelper = $jsonHelper;
$this->_importExportData = $importExportData;
$this->_resourceHelper = $resourceHelper;
$this->string = $string;
+ $this->errorAggregator = $errorAggregator;
+
+ foreach ($this->errorMessageTemplates as $errorCode => $message) {
+ $this->getErrorAggregator()->addErrorMessageTemplate($errorCode, $message);
+ }
$entityType = $config->getEntityType($this->getEntityTypeCode());
@@ -317,6 +331,25 @@ protected function _prepareRowForDb(array $rowData)
return $rowData;
}
+ /**
+ * Add errors to error aggregator
+ *
+ * @param string $code
+ * @param array|mixed $errors
+ * @return void
+ */
+ protected function addErrors($code, $errors)
+ {
+ if ($errors) {
+ $this->getErrorAggregator()->addError(
+ $code,
+ ProcessingError::ERROR_LEVEL_CRITICAL,
+ null,
+ implode('", "', $errors)
+ );
+ }
+ }
+
/**
* Validate data rows and save bunches to DB.
*
@@ -346,11 +379,14 @@ protected function _saveValidatedBunches()
$nextRowBackup = [];
}
if ($source->valid()) {
- if ($this->_errorsCount >= $this->_errorsLimit) {
- // errors limit check
- return;
+ try {
+ $rowData = $source->current();
+ } catch (\InvalidArgumentException $e) {
+ $this->addRowError($e->getMessage(), $this->_processedRowsCount);
+ $this->_processedRowsCount++;
+ $source->next();
+ continue;
}
- $rowData = $source->current();
$this->_processedRowsCount++;
@@ -381,15 +417,28 @@ protected function _saveValidatedBunches()
* @param string $errorCode Error code or simply column name
* @param int $errorRowNum Row number.
* @param string $colName OPTIONAL Column name.
+ * @param string $errorMessage OPTIONAL Column name.
+ * @param string $errorLevel
+ * @param string $errorDescription
* @return $this
*/
- public function addRowError($errorCode, $errorRowNum, $colName = null)
- {
+ public function addRowError(
+ $errorCode,
+ $errorRowNum,
+ $colName = null,
+ $errorMessage = null,
+ $errorLevel = ProcessingError::ERROR_LEVEL_CRITICAL,
+ $errorDescription = null
+ ) {
$errorCode = (string)$errorCode;
- $this->_errors[$errorCode][] = [$errorRowNum + 1, $colName];
- // one added for human readability
- $this->_invalidRows[$errorRowNum] = true;
- $this->_errorsCount++;
+ $this->getErrorAggregator()->addError(
+ $errorCode,
+ $errorLevel,
+ $errorRowNum,
+ $colName,
+ $errorMessage,
+ $errorDescription
+ );
return $this;
}
@@ -403,7 +452,7 @@ public function addRowError($errorCode, $errorRowNum, $colName = null)
*/
public function addMessageTemplate($errorCode, $message)
{
- $this->_messageTemplates[$errorCode] = $message;
+ $this->getErrorAggregator()->addErrorMessageTemplate($errorCode, $message);
return $this;
}
@@ -485,66 +534,6 @@ public function getEntityTypeId()
return $this->_entityTypeId;
}
- /**
- * Returns error information grouped by error types and translated (if possible).
- *
- * @return array
- */
- public function getErrorMessages()
- {
- $messages = [];
- foreach ($this->_errors as $errorCode => $errorRows) {
- if (isset($this->_messageTemplates[$errorCode])) {
- $errorCode = (string)__($this->_messageTemplates[$errorCode]);
- }
- foreach ($errorRows as $errorRowData) {
- $key = $errorRowData[1] ? sprintf($errorCode, $errorRowData[1]) : $errorCode;
- $messages[$key][] = $errorRowData[0];
- }
- }
- return $messages;
- }
-
- /**
- * Returns error counter value.
- *
- * @return int
- */
- public function getErrorsCount()
- {
- return $this->_errorsCount;
- }
-
- /**
- * Returns error limit value.
- *
- * @return int
- */
- public function getErrorsLimit()
- {
- return $this->_errorsLimit;
- }
-
- /**
- * Returns invalid rows count.
- *
- * @return int
- */
- public function getInvalidRowsCount()
- {
- return count($this->_invalidRows);
- }
-
- /**
- * Returns model notices.
- *
- * @return string[]
- */
- public function getNotices()
- {
- return $this->_notices;
- }
-
/**
* Returns number of checked entities.
*
@@ -643,10 +632,10 @@ public function isAttributeValid($attrCode, array $attrParams, array $rowData, $
}
if (!$valid) {
- $this->addRowError(__("Please correct the value for '%s'."), $rowNum, $attrCode);
+ $this->addRowError(self::ERROR_CODE_ATTRIBUTE_NOT_VALID, $rowNum, $attrCode);
} elseif (!empty($attrParams['is_unique'])) {
if (isset($this->_uniqueAttributes[$attrCode][$rowData[$attrCode]])) {
- $this->addRowError(__("Duplicate Unique Attribute for '%s'"), $rowNum, $attrCode);
+ $this->addRowError(self::ERROR_CODE_DUPLICATE_UNIQUE_ATTRIBUTE, $rowNum, $attrCode);
return false;
}
$this->_uniqueAttributes[$attrCode][$rowData[$attrCode]] = true;
@@ -654,17 +643,6 @@ public function isAttributeValid($attrCode, array $attrParams, array $rowData, $
return (bool)$valid;
}
- /**
- * Is all of data valid?
- *
- * @return bool
- */
- public function isDataValid()
- {
- $this->validateData();
- return 0 == $this->_errorsCount;
- }
-
/**
* Import possibility getter.
*
@@ -684,7 +662,22 @@ public function isImportAllowed()
*/
public function isRowAllowedToImport(array $rowData, $rowNum)
{
- return $this->validateRow($rowData, $rowNum) && !isset($this->_rowsToSkip[$rowNum]);
+ $this->validateRow($rowData, $rowNum);
+ return !$this->getErrorAggregator()->isRowInvalid($rowNum);
+ }
+
+ /**
+ * Retrieve message template
+ *
+ * @param string $errorCode
+ * @return null|string
+ */
+ public function retrieveMessageTemplate($errorCode)
+ {
+ if (isset($this->_messageTemplates[$errorCode])) {
+ return $this->_messageTemplates[$errorCode];
+ }
+ return null;
}
/**
@@ -718,6 +711,16 @@ public function setParameters(array $params)
return $this;
}
+ /**
+ * Get data from outside to change behavior. I.e. for setting some default parameters etc.
+ *
+ * @return array $params
+ */
+ public function getParameters()
+ {
+ return $this->_parameters;
+ }
+
/**
* Source model setter.
*
@@ -735,25 +738,24 @@ public function setSource(AbstractSource $source)
/**
* Validate data.
*
- * @return $this
+ * @return ProcessingErrorAggregatorInterface
* @throws \Magento\Framework\Exception\LocalizedException
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
public function validateData()
{
if (!$this->_dataValidated) {
+ $this->getErrorAggregator()->clear();
// do all permanent columns exist?
- if ($absentColumns = array_diff($this->_permanentAttributes, $this->getSource()->getColNames())) {
- throw new \Magento\Framework\Exception\LocalizedException(
- __('We can\'t find required columns: %1.', implode(', ', $absentColumns))
- );
- }
+ $absentColumns = array_diff($this->_permanentAttributes, $this->getSource()->getColNames());
+ $this->addErrors(self::ERROR_CODE_COLUMN_NOT_FOUND, $absentColumns);
if (ImportExport::BEHAVIOR_DELETE != $this->getBehavior()) {
// check attribute columns names validity
$columnNumber = 0;
$emptyHeaderColumns = [];
$invalidColumns = [];
+ $invalidAttributes = [];
foreach ($this->getSource()->getColNames() as $columnName) {
$columnNumber++;
if (!$this->isAttributeParticular($columnName)) {
@@ -761,29 +763,30 @@ public function validateData()
$emptyHeaderColumns[] = $columnNumber;
} elseif (!preg_match('/^[a-z][a-z0-9_]*$/', $columnName)) {
$invalidColumns[] = $columnName;
+ } elseif ($this->needColumnCheck && !in_array($columnName, $this->validColumnNames)) {
+ $invalidAttributes[] = $columnName;
}
}
}
-
- if ($emptyHeaderColumns) {
- throw new \Magento\Framework\Exception\LocalizedException(
- __('Columns number: "%1" have empty headers', implode('", "', $emptyHeaderColumns))
- );
- }
- if ($invalidColumns) {
- throw new \Magento\Framework\Exception\LocalizedException(
- __('Column names: "%1" are invalid', implode('", "', $invalidColumns))
- );
- }
+ $this->addErrors(self::ERROR_CODE_INVALID_ATTRIBUTE, $invalidAttributes);
+ $this->addErrors(self::ERROR_CODE_COLUMN_EMPTY_HEADER, $emptyHeaderColumns);
+ $this->addErrors(self::ERROR_CODE_COLUMN_NAME_INVALID, $invalidColumns);
}
- // initialize validation related attributes
- $this->_errors = [];
- $this->_invalidRows = [];
- $this->_saveValidatedBunches();
- $this->_dataValidated = true;
+ if (!$this->getErrorAggregator()->getErrorsCount()) {
+ $this->_saveValidatedBunches();
+ $this->_dataValidated = true;
+ }
}
- return $this;
+ return $this->getErrorAggregator();
+ }
+
+ /**
+ * @return ProcessingErrorAggregatorInterface
+ */
+ public function getErrorAggregator()
+ {
+ return $this->errorAggregator;
}
/**
diff --git a/app/code/Magento/ImportExport/Model/Import/ErrorProcessing/ProcessingError.php b/app/code/Magento/ImportExport/Model/Import/ErrorProcessing/ProcessingError.php
new file mode 100644
index 0000000000000..c86984ebc4d73
--- /dev/null
+++ b/app/code/Magento/ImportExport/Model/Import/ErrorProcessing/ProcessingError.php
@@ -0,0 +1,121 @@
+errorCode = $errorCode;
+ $this->errorLevel = $errorLevel;
+ $this->rowNumber = $rowNumber;
+ $this->columnName = $columnName;
+ $this->errorMessage = $errorMessage;
+ $this->errorDescription = $errorDescription;
+ }
+
+ /**
+ * @return string
+ */
+ public function getErrorCode()
+ {
+ return $this->errorCode;
+ }
+
+ /**
+ * @return string
+ */
+ public function getErrorMessage()
+ {
+ return $this->errorMessage;
+ }
+
+ /**
+ * @return int
+ */
+ public function getRowNumber()
+ {
+ return $this->rowNumber;
+ }
+
+ /**
+ * @return string
+ */
+ public function getColumnName()
+ {
+ return $this->columnName;
+ }
+
+ /**
+ * @return string
+ */
+ public function getErrorLevel()
+ {
+ return $this->errorLevel;
+ }
+
+ /**
+ * @return string
+ */
+ public function getErrorDescription()
+ {
+ return $this->errorDescription;
+ }
+}
diff --git a/app/code/Magento/ImportExport/Model/Import/ErrorProcessing/ProcessingErrorAggregator.php b/app/code/Magento/ImportExport/Model/Import/ErrorProcessing/ProcessingErrorAggregator.php
new file mode 100644
index 0000000000000..acb19a9847212
--- /dev/null
+++ b/app/code/Magento/ImportExport/Model/Import/ErrorProcessing/ProcessingErrorAggregator.php
@@ -0,0 +1,363 @@
+errorFactory = $errorFactory;
+ }
+
+ /**
+ * @param string $errorCode
+ * @param string $errorLevel
+ * @param int|null $rowNumber
+ * @param string|null $columnName
+ * @param string|null $errorMessage
+ * @param string|null $errorDescription
+ * @return $this
+ */
+ public function addError(
+ $errorCode,
+ $errorLevel = ProcessingError::ERROR_LEVEL_CRITICAL,
+ $rowNumber = null,
+ $columnName = null,
+ $errorMessage = null,
+ $errorDescription = null
+ ) {
+ if ($this->isErrorAlreadyAdded($rowNumber, $errorCode)) {
+ return $this;
+ }
+ $this->processErrorStatistics($errorLevel);
+ $this->processInvalidRow($rowNumber);
+ $errorMessage = $this->getErrorMessage($errorCode, $errorMessage, $columnName);
+
+ /** @var ProcessingError $newError */
+ $newError = $this->errorFactory->create();
+ $newError->init($errorCode, $errorLevel, $rowNumber, $columnName, $errorMessage, $errorDescription);
+ $this->items[] = $newError;
+
+ return $this;
+ }
+
+ /**
+ * @param int $rowNumber
+ * @return $this
+ */
+ public function addRowToSkip($rowNumber)
+ {
+ $rowNumber = (int)$rowNumber;
+ if (!in_array($rowNumber, $this->skippedRows)) {
+ $this->skippedRows[] = $rowNumber;
+ }
+
+ return $this;
+ }
+
+ /**
+ * @param int $rowNumber
+ * @return $this
+ */
+ protected function processInvalidRow($rowNumber)
+ {
+ if (null !== $rowNumber) {
+ $rowNumber = (int)$rowNumber;
+ if (!in_array($rowNumber, $this->invalidRows)) {
+ $this->invalidRows[] = $rowNumber;
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * @param string $code
+ * @param string $template
+ * @return $this
+ */
+ public function addErrorMessageTemplate($code, $template)
+ {
+ $this->messageTemplate[$code] = $template;
+
+ return $this;
+ }
+
+ /**
+ * @param int $rowNumber
+ * @return bool
+ */
+ public function isRowInvalid($rowNumber)
+ {
+ return in_array((int)$rowNumber, array_merge($this->invalidRows, $this->skippedRows));
+ }
+
+ /**
+ * @return int
+ */
+ public function getInvalidRowsCount()
+ {
+ return count($this->invalidRows);
+ }
+
+ /**
+ * @param string $validationStrategy
+ * @param int $allowedErrorCount
+ * @return $this
+ * @throws \Magento\Framework\Exception\LocalizedException
+ */
+ public function initValidationStrategy($validationStrategy, $allowedErrorCount = 0)
+ {
+ $allowedStrategy = [
+ self::VALIDATION_STRATEGY_STOP_ON_ERROR,
+ self::VALIDATION_STRATEGY_SKIP_ERRORS
+ ];
+ if (!in_array($validationStrategy, $allowedStrategy)) {
+ throw new \Magento\Framework\Exception\LocalizedException(
+ __('ImportExport: Import Data validation - Validation strategy not found')
+ );
+ }
+ $this->validationStrategy = $validationStrategy;
+ $this->allowedErrorsCount = (int)$allowedErrorCount;
+
+ return $this;
+ }
+
+ /**
+ * @return bool
+ */
+ public function hasToBeTerminated()
+ {
+ return $this->hasFatalExceptions() || $this->isErrorLimitExceeded();
+ }
+
+ /**
+ * @return bool
+ */
+ public function isErrorLimitExceeded()
+ {
+ $isExceeded = false;
+ $errorsCount = $this->getErrorsCount([ProcessingError::ERROR_LEVEL_NOT_CRITICAL]);
+ if ($errorsCount > 0
+ && $this->validationStrategy == self::VALIDATION_STRATEGY_STOP_ON_ERROR
+ && $errorsCount >= $this->allowedErrorsCount
+ ) {
+ $isExceeded = true;
+ }
+
+ return $isExceeded;
+ }
+
+ /**
+ * @return bool
+ */
+ public function hasFatalExceptions()
+ {
+ return (bool)$this->getErrorsCount([ProcessingError::ERROR_LEVEL_CRITICAL]);
+ }
+
+
+ /**
+ * @return ProcessingError[]
+ */
+ public function getAllErrors()
+ {
+ return $this->items;
+ }
+
+ /**
+ * @param string[] $codes
+ * @return ProcessingError[]
+ */
+ public function getErrorsByCode(array $codes)
+ {
+ $result = [];
+ foreach ($this->items as $error) {
+ if (in_array($error->getErrorCode(), $codes)) {
+ $result[] = $error;
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * @param int $rowNumber
+ * @return ProcessingError[]
+ */
+ public function getErrorByRowNumber($rowNumber)
+ {
+ $result = [];
+ foreach ($this->items as $error) {
+ if ($error->getRowNumber() == (int)$rowNumber) {
+ $result[] = $error;
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * @param array $errorCode
+ * @param array $excludedCodes
+ * @param bool $replaceCodeWithMessage
+ * @return array
+ */
+ public function getRowsGroupedByErrorCode(
+ array $errorCode = [],
+ array $excludedCodes = [],
+ $replaceCodeWithMessage = true
+ ) {
+ $result = [];
+ foreach ($this->items as $error) {
+ if ((!empty($errorCode) && in_array($error->getErrorCode(), $errorCode))
+ || in_array($error->getErrorCode(), $excludedCodes)
+ ) {
+ continue;
+ }
+ $message = $replaceCodeWithMessage ? $error->getErrorMessage() : $error->getErrorCode();
+ $result[$message][] = $error->getRowNumber()+1;
+ }
+ return $result;
+ }
+
+ /**
+ * @return int
+ */
+ public function getAllowedErrorsCount()
+ {
+ return $this->allowedErrorsCount;
+ }
+
+ /**
+ * @param string[] $errorLevels
+ * @return int
+ */
+ public function getErrorsCount(
+ array $errorLevels = [
+ ProcessingError::ERROR_LEVEL_CRITICAL,
+ ProcessingError::ERROR_LEVEL_NOT_CRITICAL
+ ]
+ ) {
+ $result = 0;
+ foreach ($errorLevels as $errorLevel) {
+ $result += isset($this->errorStatistics[$errorLevel]) ? $this->errorStatistics[$errorLevel] : 0;
+ }
+
+ return $result;
+ }
+
+ /**
+ * @return $this
+ */
+ public function clear()
+ {
+ $this->items = [];
+ $this->errorStatistics = [];
+ $this->invalidRows = [];
+
+ return $this;
+ }
+
+ /**
+ * @param int $rowNum
+ * @param string $errorCode
+ * @return bool
+ */
+ protected function isErrorAlreadyAdded($rowNum, $errorCode)
+ {
+ $errors = $this->getErrorsByCode([$errorCode]);
+ foreach ($errors as $error) {
+ if ($rowNum == $error->getRowNumber()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @param string $errorCode
+ * @param string $errorMessage
+ * @param string $columnName
+ * @return string
+ */
+ protected function getErrorMessage($errorCode, $errorMessage, $columnName)
+ {
+ if (null === $errorMessage && isset($this->messageTemplate[$errorCode])) {
+ $errorMessage = (string)__($this->messageTemplate[$errorCode]);
+ }
+ if ($columnName && $errorMessage) {
+ $errorMessage = sprintf($errorMessage, $columnName);
+ }
+ if (!$errorMessage) {
+ $errorMessage = $errorCode;
+ }
+
+ return $errorMessage;
+ }
+
+ /**
+ * @param string $errorLevel
+ * @return $this
+ */
+ protected function processErrorStatistics($errorLevel)
+ {
+ if (!empty($errorLevel)) {
+ isset($this->errorStatistics[$errorLevel]) ?
+ $this->errorStatistics[$errorLevel]++ : $this->errorStatistics[$errorLevel] = 1;
+ }
+
+ return $this;
+ }
+}
diff --git a/app/code/Magento/ImportExport/Model/Import/ErrorProcessing/ProcessingErrorAggregatorInterface.php b/app/code/Magento/ImportExport/Model/Import/ErrorProcessing/ProcessingErrorAggregatorInterface.php
new file mode 100644
index 0000000000000..3bbf90a068a08
--- /dev/null
+++ b/app/code/Magento/ImportExport/Model/Import/ErrorProcessing/ProcessingErrorAggregatorInterface.php
@@ -0,0 +1,163 @@
+_file->readCsv(0, $this->_delimiter, $this->_enclosure);
+ $parsed = $this->_file->readCsv(0, $this->_delimiter, $this->_enclosure);
+ if (is_array($parsed) && count($parsed) != $this->_colQty) {
+ foreach ($parsed as $element) {
+ if (strpos($element, "'") !== false) {
+ $this->_foundWrongQuoteFlag = true;
+ break;
+ }
+ }
+ } else {
+ $this->_foundWrongQuoteFlag = false;
+ }
+ return $parsed;
}
/**
diff --git a/app/code/Magento/ImportExport/Model/Report/Csv.php b/app/code/Magento/ImportExport/Model/Report/Csv.php
new file mode 100644
index 0000000000000..387e57e4eefd9
--- /dev/null
+++ b/app/code/Magento/ImportExport/Model/Report/Csv.php
@@ -0,0 +1,151 @@
+reportHelper = $reportHelper;
+ $this->sourceCsvFactory = $sourceCsvFactory;
+ $this->outputCsvFactory = $outputCsvFactory;
+ $this->filesystem = $filesystem;
+ }
+
+ /**
+ * @param string $originalFileName
+ * @param ProcessingErrorAggregatorInterface $errorAggregator
+ * @param bool $writeOnlyErrorItems
+ * @return string
+ * @throws \Magento\Framework\Exception\LocalizedException
+ */
+ public function createReport(
+ $originalFileName,
+ ProcessingErrorAggregatorInterface $errorAggregator,
+ $writeOnlyErrorItems = false
+ ) {
+ $sourceCsv = $this->createSourceCsvModel($originalFileName);
+
+ $outputFileName = $this->generateOutputFileName($originalFileName);
+ $outputCsv = $this->createOutputCsvModel($outputFileName);
+
+ $columnsName = $sourceCsv->getColNames();
+ array_push($columnsName, self::REPORT_ERROR_COLUMN_NAME);
+ $outputCsv->setHeaderCols($columnsName);
+
+ foreach ($sourceCsv as $rowNum => $rowData) {
+ $errorMessages = $this->retrieveErrorMessagesByRowNumber($rowNum, $errorAggregator);
+ if (!$writeOnlyErrorItems || ($writeOnlyErrorItems && $errorMessages)) {
+ $rowData[self::REPORT_ERROR_COLUMN_NAME] = $errorMessages;
+ $outputCsv->writeRow($rowData);
+ }
+ }
+
+ return $outputFileName;
+ }
+
+ /**
+ * @param int $rowNumber
+ * @param ProcessingErrorAggregatorInterface $errorAggregator
+ * @return string
+ */
+ public function retrieveErrorMessagesByRowNumber($rowNumber, ProcessingErrorAggregatorInterface $errorAggregator)
+ {
+ $messages = '';
+ foreach ($errorAggregator->getErrorByRowNumber((int)$rowNumber) as $error) {
+ $messages .= $error->getErrorMessage() . ',';
+ }
+ $messages = rtrim($messages, ',');
+
+ if ($messages) {
+ $messages = str_pad($messages, 1, '"', STR_PAD_BOTH);
+ }
+
+ return $messages;
+ }
+
+ /**
+ * @param string $sourceFile
+ * @return string
+ */
+ protected function generateOutputFileName($sourceFile)
+ {
+ $fileName = basename($sourceFile, self::ERROR_REPORT_FILE_EXTENSION);
+ return $fileName . self::ERROR_REPORT_FILE_SUFFIX . self::ERROR_REPORT_FILE_EXTENSION;
+ }
+
+ /**
+ * @param string $sourceFile
+ * @return \Magento\ImportExport\Model\Import\Source\Csv
+ */
+ protected function createSourceCsvModel($sourceFile)
+ {
+ return $this->sourceCsvFactory->create(
+ [
+ 'file' => $sourceFile,
+ 'directory' => $this->filesystem->getDirectoryWrite(DirectoryList::VAR_DIR)
+ ]
+ );
+ }
+
+ /**
+ * @param string $outputFileName
+ * @return \Magento\ImportExport\Model\Export\Adapter\Csv
+ */
+ protected function createOutputCsvModel($outputFileName)
+ {
+ return $this->outputCsvFactory->create(
+ [
+ 'destination' => Import::IMPORT_HISTORY_DIR . $outputFileName,
+ 'destinationDirectoryCode' => DirectoryList::VAR_DIR,
+ ]
+ );
+ }
+}
diff --git a/app/code/Magento/ImportExport/Model/Report/ReportProcessorInterface.php b/app/code/Magento/ImportExport/Model/Report/ReportProcessorInterface.php
new file mode 100644
index 0000000000000..891270eb89c60
--- /dev/null
+++ b/app/code/Magento/ImportExport/Model/Report/ReportProcessorInterface.php
@@ -0,0 +1,27 @@
+ $this->_linkTable],
'link_table.user_id = main_table.user_id',
['username']
- )->where('execution_time != ?', History::IMPORT_VALIDATION);
+ )->where(
+ 'execution_time != ? OR (error_file != "" AND execution_time = ?)',
+ History::IMPORT_VALIDATION,
+ History::IMPORT_VALIDATION
+ );
return $this;
}
diff --git a/app/code/Magento/ImportExport/Model/Source/Import/AbstractBehavior.php b/app/code/Magento/ImportExport/Model/Source/Import/AbstractBehavior.php
index 691fcbaa61c19..2155dc20716d2 100644
--- a/app/code/Magento/ImportExport/Model/Source/Import/AbstractBehavior.php
+++ b/app/code/Magento/ImportExport/Model/Source/Import/AbstractBehavior.php
@@ -39,9 +39,21 @@ public function toOptionArray()
/**
* Get current behaviour group code
- *
+ *;
* @abstract
* @return string
*/
abstract public function getCode();
+
+ /**
+ * Get array of notes for possible values
+ *
+ * @param string $entityCode
+ * @return array
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ */
+ public function getNotes($entityCode)
+ {
+ return [];
+ }
}
diff --git a/app/code/Magento/ImportExport/Model/Source/Import/Behavior/Basic.php b/app/code/Magento/ImportExport/Model/Source/Import/Behavior/Basic.php
index 4b64cd12d5171..f24eb32dc5084 100644
--- a/app/code/Magento/ImportExport/Model/Source/Import/Behavior/Basic.php
+++ b/app/code/Magento/ImportExport/Model/Source/Import/Behavior/Basic.php
@@ -29,4 +29,15 @@ public function getCode()
{
return 'basic';
}
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getNotes($entityCode)
+ {
+ $messages = ['catalog_product' => [
+ \Magento\ImportExport\Model\Import::BEHAVIOR_REPLACE => __("Note: Product IDs will be regenerated.")
+ ]];
+ return isset($messages[$entityCode]) ? $messages[$entityCode] : [];
+ }
}
diff --git a/app/code/Magento/ImportExport/Setup/UpgradeSchema.php b/app/code/Magento/ImportExport/Setup/UpgradeSchema.php
new file mode 100644
index 0000000000000..ae0999e99326b
--- /dev/null
+++ b/app/code/Magento/ImportExport/Setup/UpgradeSchema.php
@@ -0,0 +1,38 @@
+startSetup();
+ $installer->getConnection()->addColumn(
+ $installer->getTable('import_history'),
+ 'error_file',
+ [
+ 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
+ 'length' => 255,
+ 'nullable' => false,
+ 'comment' => 'Imported file with errors'
+ ]
+ );
+ $installer->endSetup();
+ }
+}
diff --git a/app/code/Magento/ImportExport/Test/Unit/Model/Import/AbstractImportTestCase.php b/app/code/Magento/ImportExport/Test/Unit/Model/Import/AbstractImportTestCase.php
new file mode 100644
index 0000000000000..f4aee3f827ac6
--- /dev/null
+++ b/app/code/Magento/ImportExport/Test/Unit/Model/Import/AbstractImportTestCase.php
@@ -0,0 +1,44 @@
+objectManagerHelper = new ObjectManagerHelper($this);
+ }
+
+ /**
+ * @param array|null $methods
+ * @return ProcessingErrorAggregatorInterface|\PHPUnit_Framework_MockObject_MockObject
+ */
+ protected function getErrorAggregatorObject($methods = null)
+ {
+ $errorFactory = $this->getMockBuilder(
+ 'Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingErrorFactory'
+ )->disableOriginalConstructor()
+ ->setMethods(['create'])
+ ->getMock();
+ $errorFactory->method('create')->willReturn(
+ $this->objectManagerHelper->getObject('Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingError')
+ );
+ return $this->getMockBuilder('Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingErrorAggregator')
+ ->setMethods($methods)
+ ->setConstructorArgs(['errorFactory' => $errorFactory])
+ ->getMock();
+ }
+}
diff --git a/app/code/Magento/ImportExport/Test/Unit/Model/Import/Entity/AbstractTest.php b/app/code/Magento/ImportExport/Test/Unit/Model/Import/Entity/AbstractTest.php
index e4ffe0d900975..b1bb4e6192522 100644
--- a/app/code/Magento/ImportExport/Test/Unit/Model/Import/Entity/AbstractTest.php
+++ b/app/code/Magento/ImportExport/Test/Unit/Model/Import/Entity/AbstractTest.php
@@ -9,7 +9,9 @@
*/
namespace Magento\ImportExport\Test\Unit\Model\Import\Entity;
-class AbstractTest extends \PHPUnit_Framework_TestCase
+use Magento\ImportExport\Model\Import\Entity\AbstractEntity;
+
+class AbstractTest extends \Magento\ImportExport\Test\Unit\Model\Import\AbstractImportTestCase
{
/**
* Abstract import entity model
@@ -22,14 +24,13 @@ protected function setUp()
{
parent::setUp();
- $this->_model = $this->getMockForAbstractClass(
- 'Magento\ImportExport\Model\Import\Entity\AbstractEntity',
- [],
- '',
- false,
- true,
- true,
- ['_saveValidatedBunches']
+ $this->_model = $this->getMockBuilder('Magento\ImportExport\Model\Import\Entity\AbstractEntity')
+ ->disableOriginalConstructor()
+ ->setMethods(['_saveValidatedBunches', 'getErrorAggregator'])
+ ->getMockForAbstractClass();
+
+ $this->_model->method('getErrorAggregator')->willReturn(
+ $this->getErrorAggregatorObject()
);
}
@@ -68,13 +69,15 @@ protected function _createSourceAdapterMock(array $columns)
* Test for method validateData()
*
* @covers \Magento\ImportExport\Model\Import\Entity\AbstractEntity::validateData
- * @expectedException \Magento\Framework\Exception\LocalizedException
- * @expectedExceptionMessage Columns number: "1" have empty headers
*/
public function testValidateDataEmptyColumnName()
{
$this->_createSourceAdapterMock(['']);
- $this->_model->validateData();
+ $errorAggregator = $this->_model->validateData();
+ $this->assertArrayHasKey(
+ AbstractEntity::ERROR_CODE_COLUMN_EMPTY_HEADER,
+ $errorAggregator->getRowsGroupedByErrorCode()
+ );
}
/**
@@ -86,7 +89,8 @@ public function testValidateDataEmptyColumnNameForDeleteBehaviour()
{
$this->_createSourceAdapterMock(['']);
$this->_model->setParameters(['behavior' => \Magento\ImportExport\Model\Import::BEHAVIOR_DELETE]);
- $this->_model->validateData();
+ $errorAggregator = $this->_model->validateData();
+ $this->assertEquals(0, $errorAggregator->getErrorsCount());
}
/**
@@ -98,33 +102,38 @@ public function testValidateDataColumnNameWithWhitespacesForDeleteBehaviour()
{
$this->_createSourceAdapterMock([' ']);
$this->_model->setParameters(['behavior' => \Magento\ImportExport\Model\Import::BEHAVIOR_DELETE]);
- $this->_model->validateData();
+ $errorAggregator = $this->_model->validateData();
+ $this->assertEquals(0, $errorAggregator->getErrorsCount());
}
/**
* Test for method validateData()
*
* @covers \Magento\ImportExport\Model\Import\Entity\AbstractEntity::validateData
- * @expectedException \Magento\Framework\Exception\LocalizedException
- * @expectedExceptionMessage Columns number: "1" have empty headers
*/
public function testValidateDataColumnNameWithWhitespaces()
{
$this->_createSourceAdapterMock([' ']);
- $this->_model->validateData();
+ $errorAggregator = $this->_model->validateData();
+ $this->assertArrayHasKey(
+ AbstractEntity::ERROR_CODE_COLUMN_EMPTY_HEADER,
+ $errorAggregator->getRowsGroupedByErrorCode()
+ );
}
/**
* Test for method validateData()
*
* @covers \Magento\ImportExport\Model\Import\Entity\AbstractEntity::validateData
- * @expectedException \Magento\Framework\Exception\LocalizedException
- * @expectedExceptionMessage Column names: "_test1" are invalid
*/
public function testValidateDataAttributeNames()
{
$this->_createSourceAdapterMock(['_test1']);
- $this->_model->validateData();
+ $errorAggregator = $this->_model->validateData();
+ $this->assertArrayHasKey(
+ AbstractEntity::ERROR_CODE_COLUMN_NAME_INVALID,
+ $errorAggregator->getRowsGroupedByErrorCode()
+ );
}
/**
diff --git a/app/code/Magento/ImportExport/Test/Unit/Model/Import/Entity/EavAbstractTest.php b/app/code/Magento/ImportExport/Test/Unit/Model/Import/Entity/EavAbstractTest.php
index 6add3933dbf53..321f2f455bbdc 100644
--- a/app/code/Magento/ImportExport/Test/Unit/Model/Import/Entity/EavAbstractTest.php
+++ b/app/code/Magento/ImportExport/Test/Unit/Model/Import/Entity/EavAbstractTest.php
@@ -9,7 +9,7 @@
*/
namespace Magento\ImportExport\Test\Unit\Model\Import\Entity;
-class EavAbstractTest extends \PHPUnit_Framework_TestCase
+class EavAbstractTest extends \Magento\ImportExport\Test\Unit\Model\Import\AbstractImportTestCase
{
/**
* Entity type id
@@ -60,6 +60,8 @@ class EavAbstractTest extends \PHPUnit_Framework_TestCase
protected function setUp()
{
+ parent::setUp();
+
$this->_string = new \Magento\Framework\Stdlib\StringUtils();
$scopeConfig = $this->getMock('Magento\Framework\App\Config\ScopeConfigInterface');
@@ -96,6 +98,7 @@ protected function setUp()
$this->_importFactory,
$this->_resourceHelper,
$this->_resource,
+ $this->getErrorAggregatorObject(),
$this->_storeManager,
$this->_collectionFactory,
$this->_eavConfig,
diff --git a/app/code/Magento/ImportExport/Test/Unit/Model/Import/EntityAbstractTest.php b/app/code/Magento/ImportExport/Test/Unit/Model/Import/EntityAbstractTest.php
index 21b65012b19db..d60b4c66b9f44 100644
--- a/app/code/Magento/ImportExport/Test/Unit/Model/Import/EntityAbstractTest.php
+++ b/app/code/Magento/ImportExport/Test/Unit/Model/Import/EntityAbstractTest.php
@@ -13,7 +13,7 @@
use Magento\ImportExport\Model\Import\AbstractEntity;
-class EntityAbstractTest extends \PHPUnit_Framework_TestCase
+class EntityAbstractTest extends \Magento\ImportExport\Test\Unit\Model\Import\AbstractImportTestCase
{
/**
* Abstract import entity model
@@ -35,10 +35,12 @@ class EntityAbstractTest extends \PHPUnit_Framework_TestCase
protected function setUp()
{
- $this->_model = $this->getMockForAbstractClass(
- 'Magento\ImportExport\Model\Import\AbstractEntity',
- $this->_getModelDependencies()
- );
+ parent::setUp();
+
+ $this->_model = $this->getMockBuilder('Magento\ImportExport\Model\Import\AbstractEntity')
+ ->setConstructorArgs($this->_getModelDependencies())
+ ->setMethods(['_saveValidatedBunches'])
+ ->getMockForAbstractClass();
}
protected function tearDown()
@@ -65,6 +67,7 @@ protected function _getModelDependencies()
'importFactory' => $importFactory,
'resourceHelper' => $resourceHelper,
'resource' => $resource,
+ 'errorAggregator' => $this->getErrorAggregatorObject(),
'data' => [
'data_source_model' => 'not_used',
'connection' => 'not_used',
@@ -109,14 +112,14 @@ public function testPrepareRowForDb()
*/
public function testAddRowError()
{
- $errorCode = 'error_code ';
+ $errorCode = 'error_code';
$errorColumnName = 'error_column';
- $this->_model->addRowError($errorCode . '%s', 0, $errorColumnName);
+ $this->_model->addRowError($errorCode . '%s', 0, $errorColumnName, $errorCode . ' %s');
- $this->assertGreaterThan(0, $this->_model->getErrorsCount());
+ $this->assertGreaterThan(0, $this->_model->getErrorAggregator()->getErrorsCount());
- $errors = $this->_model->getErrorMessages();
- $this->assertArrayHasKey($errorCode . $errorColumnName, $errors);
+ $errors = $this->_model->getErrorAggregator()->getRowsGroupedByErrorCode();
+ $this->assertArrayHasKey($errorCode . ' ' . $errorColumnName, $errors);
}
/**
@@ -152,32 +155,11 @@ public function testAddMessageTemplate()
$this->_model->addMessageTemplate($errorCode, $message);
$this->_model->addRowError($errorCode, 0);
- $errors = $this->_model->getErrorMessages();
+ $errors = $this->_model->getErrorAggregator()->getRowsGroupedByErrorCode();
$this->assertArrayHasKey($message, $errors);
}
- /**
- * Test for method isDataValid()
- */
- public function testIsDataValid()
- {
- /** @var $model AbstractEntity|\PHPUnit_Framework_MockObject_MockObject */
- $model = $this->getMockForAbstractClass(
- 'Magento\ImportExport\Model\Import\AbstractEntity',
- [],
- '',
- false,
- true,
- true,
- ['validateData']
- );
- $model->expects($this->any())->method('validateData');
- $this->assertTrue($model->isDataValid());
- $model->addRowError('test', 1);
- $this->assertFalse($model->isDataValid());
- }
-
/**
* Test for method isRowAllowedToImport()
*/
@@ -417,8 +399,7 @@ public function testIsAttributeValid(array $data)
$rowData[$attributeCode] = $data['invalid_value'];
$this->assertFalse($this->_model->isAttributeValid($attributeCode, $attributeParams, $rowData, 0));
-
- $this->assertEquals(1, $this->_model->getErrorsCount(), 'Wrong count of errors');
+ $this->assertEquals(1, $this->_model->getErrorAggregator()->getErrorsCount(), 'Wrong count of errors');
}
/**
@@ -525,7 +506,6 @@ protected function _getDataSet($code, $type, $validValue, $invalidValue, $isUniq
* Test for method validateData()
*
* @covers \Magento\ImportExport\Model\Import\AbstractEntity::validateData
- * @expectedException \Magento\Framework\Exception\LocalizedException
*/
public function testValidateDataPermanentAttributes()
{
@@ -537,43 +517,45 @@ public function testValidateDataPermanentAttributes()
$property->setAccessible(true);
$property->setValue($this->_model, $permanentAttributes);
- $this->_model->validateData();
+ $errorAggregator = $this->_model->validateData();
+ $this->assertEquals(1, $errorAggregator->getErrorsCount());
}
/**
* Test for method validateData()
*
* @covers \Magento\ImportExport\Model\Import\AbstractEntity::validateData
- * @expectedException \Magento\Framework\Exception\LocalizedException
*/
public function testValidateDataEmptyColumnName()
{
$this->_createSourceAdapterMock(['']);
- $this->_model->validateData();
+ $errorAggregator = $this->_model->validateData();
+ $this->assertEquals(1, $errorAggregator->getErrorsCount());
}
/**
* Test for method validateData()
*
* @covers \Magento\ImportExport\Model\Import\AbstractEntity::validateData
- * @expectedException \Magento\Framework\Exception\LocalizedException
*/
public function testValidateDataColumnNameWithWhitespaces()
{
$this->_createSourceAdapterMock([' ']);
$this->_model->validateData();
+ $errorAggregator = $this->_model->validateData();
+ $this->assertEquals(1, $errorAggregator->getErrorsCount());
}
/**
* Test for method validateData()
*
* @covers \Magento\ImportExport\Model\Import\AbstractEntity::validateData
- * @expectedException \Magento\Framework\Exception\LocalizedException
*/
public function testValidateDataAttributeNames()
{
$this->_createSourceAdapterMock(['_test1']);
- $this->_model->validateData();
+ $errorAggregator = $this->_model->validateData();
+ $this->assertEquals(1, $errorAggregator->getErrorsCount());
}
/**
diff --git a/app/code/Magento/ImportExport/Test/Unit/Model/Import/ErrorProcessing/ProcessingErrorAggregatorTest.php b/app/code/Magento/ImportExport/Test/Unit/Model/Import/ErrorProcessing/ProcessingErrorAggregatorTest.php
new file mode 100644
index 0000000000000..63b82e3693d2d
--- /dev/null
+++ b/app/code/Magento/ImportExport/Test/Unit/Model/Import/ErrorProcessing/ProcessingErrorAggregatorTest.php
@@ -0,0 +1,400 @@
+processingErrorFactoryMock = $this->getMock(
+ '\Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingErrorFactory',
+ ['create'],
+ [],
+ '',
+ false
+ );
+
+ $this->processingErrorMock1 = $this->getMock(
+ '\Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingError',
+ null,
+ [],
+ '',
+ false
+ );
+
+ $this->processingErrorMock2 = $this->getMock(
+ '\Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingError',
+ null,
+ [],
+ '',
+ false
+ );
+
+ $this->processingErrorMock3 = $this->getMock(
+ '\Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingError',
+ null,
+ [],
+ '',
+ false
+ );
+
+ $this->processingErrorFactoryMock->expects($this->any())->method('create')->willReturnOnConsecutiveCalls(
+ $this->processingErrorMock1,
+ $this->processingErrorMock2,
+ $this->processingErrorMock3
+ );
+
+ $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
+
+ $this->model = $objectManager->getObject(
+ '\Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingErrorAggregator',
+ [
+ 'errorFactory' => $this->processingErrorFactoryMock
+ ]
+ );
+ }
+
+ /**
+ * Test for method addError
+ */
+ public function testAddError()
+ {
+ $this->model->addError('systemException', 'critical', 7, 'Some column name', 'Message', 'Description');
+ }
+
+ /**
+ * Test for method addRowToSkip
+ */
+ public function testAddRowToSkip()
+ {
+ $this->model->addRowToSkip(7);
+ $result = $this->model->isRowInvalid(7);
+ $this->assertTrue($result);
+ }
+
+ /**
+ * Test for method addErrorMessageTemplate
+ */
+ public function testAddErrorMessageTemplate()
+ {
+ $this->model->addErrorMessageTemplate('columnNotFound', 'Template: No column');
+ $this->model->addError('systemException');
+ $this->model->addError('columnNotFound', 'critical', 7, 'Some column name', null, 'Description');
+ $this->model->addError('columnEmptyHeader', 'not-critical', 4, 'Some column name', 'No header', 'Description');
+ $result = $this->model->getRowsGroupedByErrorCode(['systemException']);
+ $expectedResult = [
+ 'Template: No column' => [8],
+ 'No header' => [5]
+ ];
+ $this->assertEquals($expectedResult, $result);
+ }
+
+ /**
+ * Test for method isRowInvalid. Expected true result.
+ */
+ public function testIsRowInvalidTrue()
+ {
+ $this->model->addError('systemException', 'critical', 7, 'Some column name', 'Message', 'Description');
+ $result = $this->model->isRowInvalid(7);
+ $this->assertTrue($result);
+ }
+
+ /**
+ * Test for method isRowInvalid. Expected false result.
+ */
+ public function testIsRowInvalidFalse()
+ {
+ $this->model->addError('systemException');
+ $result = $this->model->isRowInvalid(8);
+ $this->assertFalse($result);
+ }
+
+ /**
+ * Test for method getInvalidRowsCount. Expected 0 invalid rows.
+ */
+ public function testGetInvalidRowsCountZero()
+ {
+ $rowsNumber = $this->model->getInvalidRowsCount();
+ $this->assertEquals($rowsNumber, 0);
+ }
+
+ /**
+ * Test for method getInvalidRowsCount. Expected 1 invalid row.
+ */
+ public function testGetInvalidRowsCountOne()
+ {
+ $this->model->addError('systemException', 'critical', 7, 'Some column name', 'Message', 'Description');
+ $rowsNumber = $this->model->getInvalidRowsCount();
+ $this->assertEquals($rowsNumber, 1);
+ }
+
+ /**
+ * Test for method getInvalidRowsCount. Expected 2 invalid rows.
+ */
+ public function testGetInvalidRowsCountTwo()
+ {
+ $this->model->addError('systemException');
+ $this->model->addError('systemException', 'critical', 8, 'Some column name', 'Message', 'Description');
+ $this->model->addError('systemException', 'critical', 7, 'Some column name', 'Message', 'Description');
+ $rowsNumber = $this->model->getInvalidRowsCount();
+ $this->assertEquals($rowsNumber, 2);
+ }
+
+ /**
+ * Test for method initValidationStrategy.
+ */
+ public function testInitValidationStrategy()
+ {
+ $this->model->initValidationStrategy('validation-stop-on-errors', 5);
+ $this->assertEquals(5, $this->model->getAllowedErrorsCount());
+ }
+
+ /**
+ * Test for method initValidationStrategy.
+ */
+ public function testInitValidationStrategyExceed()
+ {
+ $this->model->addError('systemException', 'not-critical', 7, 'Some column name', 'Message', 'Description');
+ $this->model->addError('systemException', 'not-critical', 4, 'Some column name', 'Message', 'Description');
+ $this->model->addError('systemException', 'not-critical', 2, 'Some column name', 'Message', 'Description');
+ $this->model->initValidationStrategy('validation-stop-on-errors', 2);
+ $result = $this->model->isErrorLimitExceeded();
+ $this->assertTrue($result);
+ }
+
+ /**
+ * Test for method initValidationStrategy. Expected exeption due null incoming parameter
+ */
+ public function testInitValidationStrategyException()
+ {
+ $this->setExpectedException('\Magento\Framework\Exception\LocalizedException');
+ $this->model->initValidationStrategy(null);
+ }
+
+ /**
+ * Test for method isErrorLimitExceeded. Expects error limit exceeded.
+ */
+ public function testIsErrorLimitExceededTrue()
+ {
+ $this->model->addError('systemException');
+ $this->model->addError('systemException', 'not-critical', 7, 'Some column name', 'Message', 'Description');
+ $this->model->addError('systemException', 'not-critical', 4, 'Some column name', 'Message', 'Description');
+ $result = $this->model->isErrorLimitExceeded();
+ $this->assertTrue($result);
+ }
+
+ /**
+ * Test for method isErrorLimitExceeded. Unexpects error limit exceeded.
+ */
+ public function testIsErrorLimitExceededFalse()
+ {
+ $this->model->addError('systemException');
+ $this->model->addError('systemException', 'critical', 7, 'Some column name', 'Message', 'Description');
+ $this->model->addError('systemException', 'critical', 4, 'Some column name', 'Message', 'Description');
+ $result = $this->model->isErrorLimitExceeded();
+ $this->assertFalse($result);
+ }
+
+ /**
+ * Test for method hasFatalExceptions.
+ */
+ public function testHasFatalExceptionsTrue()
+ {
+ $this->model->addError('systemException');
+ $this->model->addError('columnNotFound', 'critical', 7, 'Some column name', 'Message', 'Description');
+ $this->model->addError('columnEmptyHeader', 'not-critical', 4, 'Some column name', 'Message', 'Description');
+ $result = $this->model->hasFatalExceptions();
+ $this->assertTrue($result);
+ }
+
+ /**
+ * Test for method hasFatalExceptions. Expects no any fatal exceptions
+ */
+ public function testHasFatalExceptionsFalse()
+ {
+ $this->model->addError('systemException', 'not-critical', 4, 'Some column name', 'Message', 'Description');
+ $result = $this->model->hasFatalExceptions();
+ $this->assertFalse($result);
+ }
+
+ /**
+ * Test for method hasToBeTerminated. Unexpects any errors and termination.
+ */
+ public function testHasToBeTerminatedFalse()
+ {
+ $result = $this->model->hasToBeTerminated();
+ $this->assertFalse($result);
+ }
+
+ /**
+ * Test for method hasToBeTerminated. Expects errors and execute termination.
+ */
+ public function testHasToBeTerminatedTrue()
+ {
+ $this->model->addError('systemException', 'not-critical', 4, 'Some column name', 'Message', 'Description');
+ $result = $this->model->hasToBeTerminated();
+ $this->assertTrue($result);
+ }
+
+ /**
+ * Test for method getAllErrors
+ */
+ public function testGetAllErrors()
+ {
+ $this->model->addError('systemException', 'not-critical', 4, 'Some column name', 'Message', 'Description');
+ $this->model->addError('systemException', 'not-critical', 5, 'Some column name', 'Message', 'Description');
+ $this->model->addError('systemException', 'not-critical', 6, 'Some column name', 'Message', 'Description');
+ $result = $this->model->getAllErrors();
+ //check if is array of objects
+ $this->assertInternalType('array', $result);
+ $this->assertCount(3, $result);
+ $this->assertInstanceOf('\Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingError', $result[0]);
+ }
+
+ /**
+ * Test for method getErrorByRowNumber
+ */
+ public function testGetErrorByRowNumber()
+ {
+ $this->model->addError('systemException1', 'not-critical', 1);
+ $this->model->addError('systemException2', 'not-critical', 1);
+ $this->model->addError('systemException3', 'not-critical', 2);
+ $result = $this->model->getErrorByRowNumber(1);
+
+ $this->assertInternalType('array', $result);
+ $this->assertCount(2, $result);
+ $this->assertInstanceOf('\Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingError', $result[0]);
+ $this->assertInstanceOf('\Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingError', $result[1]);
+ $this->assertEquals('systemException1', $result[0]->getErrorCode());
+ $this->assertEquals('systemException2', $result[1]->getErrorCode());
+ }
+
+ /**
+ * Test logic to prevent adding an identical error more than once.
+ * The error has to have the same error code for the same row number
+ */
+ public function testAddTheSameErrorTwice()
+ {
+ $this->model->addError('systemException', 'not-critical', 1);
+ $this->model->addError('systemException', 'not-critical', 1);
+ $result = $this->model->getErrorByRowNumber(1);
+
+ $this->assertCount(1, $result);
+ $this->assertEquals(1, $this->model->getErrorsCount());
+ }
+
+ /**
+ * Test for method getErrorsByCode. Expects receive errors with code, which present in incomming parameter.
+ */
+ public function testGetErrorsByCodeInArray()
+ {
+ $this->model->addError('systemException', 'not-critical', 4, 'Some column name', 'Message', 'Description');
+ $this->model->addError('columnNotFound', 'critical', 7, 'Some column name', 'No column', 'Description');
+ $this->model->addError('systemException', 'not-critical', 9, 'Some column name', 'Message', 'Description');
+ $result = $this->model->getErrorsByCode(['systemException']);
+ $this->assertCount(2, $result);
+ }
+
+ /**
+ * Test for method getErrorsByCode. Unexpects receive errors with code, which present in incomming parameter.
+ */
+ public function testGetErrorsByCodeNotInArray()
+ {
+ $this->model->addError('columnNotFound', 'not-critical', 4, 'Some column name', 'Message', 'Description');
+ $this->model->addError('columnNotFound', 'critical', 7, 'Some column name', 'No column', 'Description');
+ $this->model->addError('columnEmptyHeader', 'not-critical', 5, 'Some column name', 'No header', 'Description');
+ $result = $this->model->getErrorsByCode(['systemException']);
+ $this->assertCount(0, $result);
+ }
+
+ /**
+ * Test for method getRowsGroupedByErrorCode. Expects errors.
+ */
+ public function testGetRowsGroupedByErrorCodeWithErrors()
+ {
+ $this->model->addError('systemException');
+ $this->model->addError('columnNotFound', 'critical', 7, 'Some column name', 'No column', 'Description');
+ $this->model->addError('columnEmptyHeader', 'not-critical', 4, 'Some column name', 'No header', 'Description');
+ $result = $this->model->getRowsGroupedByErrorCode(['systemException']);
+ $expectedResult = [
+ 'No column' => [8],
+ 'No header' => [5]
+ ];
+ $this->assertEquals($expectedResult, $result);
+ }
+
+ /**
+ * Test for method getRowsGroupedByErrorCode. Unexpects errors.
+ */
+ public function testGetRowsGroupedByErrorCodeNoErrors()
+ {
+ $result = $this->model->getRowsGroupedByErrorCode();
+ $this->assertInternalType('array', $result);
+ $this->assertCount(0, $result);
+ }
+
+ /**
+ * Test for method getAllowedErrorsCount.
+ */
+ public function testGetAllowedErrorsCount()
+ {
+ $this->assertEquals($this->model->getAllowedErrorsCount(), 0);
+ }
+
+ /**
+ * Test for method clear.
+ */
+ public function testClear()
+ {
+ $this->model->addError('systemException', 'not-critical', 4, 'Some column name', 'Message', 'Description');
+ $this->model->addError('columnNotFound', 'critical', 7, 'Some column name', 'No column', 'Description');
+ $this->model->clear();
+ $result = $this->model->getAllErrors();
+ $this->assertEquals([], $result);
+ }
+
+ /**
+ * Test for method getErrorsCount
+ */
+ public function testGetErrorsCount()
+ {
+ $this->model->addError('systemException', 'not-critical', 4, 'Some column name', 'Message', 'Description');
+ $this->model->addError('columnNotFound', 'critical', 7, 'Some column name', 'No column', 'Description');
+ $this->model->addError('systemException', 'not-critical', 9, 'Some column name', 'Message', 'Description');
+ $result = $this->model->getErrorsCount(['critical']);
+ $this->assertEquals($result, 1);
+ }
+}
diff --git a/app/code/Magento/ImportExport/Test/Unit/Model/Import/ErrorProcessing/ProcessingErrorTest.php b/app/code/Magento/ImportExport/Test/Unit/Model/Import/ErrorProcessing/ProcessingErrorTest.php
new file mode 100644
index 0000000000000..55aa06725d893
--- /dev/null
+++ b/app/code/Magento/ImportExport/Test/Unit/Model/Import/ErrorProcessing/ProcessingErrorTest.php
@@ -0,0 +1,283 @@
+model = $objectManager->getObject('\Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingError');
+ }
+
+ /**
+ * Test for method init.
+ *
+ * @dataProvider errorMessageInfo
+ * @SuppressWarnings(PHPMD.NPathComplexity)
+ */
+ public function testInit($initData)
+ {
+ $errorLevel = isset($initData['errorLevel']) ? $initData['errorLevel'] : null;
+ $rowNumber = isset($initData['rowNumber']) ? $initData['rowNumber'] : null;
+ $columnName = isset($initData['columnName']) ? $initData['columnName'] : null;
+ $errorMessage = isset($initData['errorMessage']) ? $initData['errorMessage'] : null;
+ $errorDescription = isset($initData['errorDescription']) ? $initData['errorDescription'] : null;
+
+ $this->model->init(
+ $initData['errorCode'],
+ $errorLevel,
+ $rowNumber,
+ $columnName,
+ $errorMessage,
+ $errorDescription
+ );
+ }
+
+ /**
+ * Data for method testInit
+ *
+ * @return array
+ */
+ public function errorMessageInfo()
+ {
+ return [
+ [
+ [
+ 'errorCode' => 5,
+ 'errorLevel' => 'critical',
+ 'rowNumber' => 7,
+ 'columnName' => 25,
+ 'errorMessage' => 'some error message',
+ 'errorDescription' => 'some error description'
+ ]
+ ],
+ [
+ [
+ 'errorCode' => 5,
+ 'errorLevel' => null,
+ 'rowNumber' => null,
+ 'columnName' => null,
+ 'errorMessage' => null,
+ 'errorDescription' => null
+ ]
+ ],
+ ];
+ }
+
+ /**
+ * Test for method getErrorCode
+ *
+ * @dataProvider errorCodeData
+ */
+ public function testGetErrorCode($data, $expectedValue)
+ {
+ $this->testInit($data);
+ $result = $this->model->getErrorCode();
+ $this->assertEquals($result, $expectedValue);
+ }
+
+ /**
+ * Data for method testGetErrorCode
+ *
+ * @return array
+ */
+ public function errorCodeData()
+ {
+ return [
+ [
+ ['errorCode' => 5],
+ 5
+ ],
+ [
+ ['errorCode' => null],
+ null
+ ],
+ ];
+ }
+
+ /**
+ * Test for method getErrorMessage
+ *
+ * @dataProvider errorMessageData
+ */
+ public function testGetErrorMessage($data, $expectedValue)
+ {
+ $this->testInit($data);
+ $result = $this->model->getErrorMessage();
+ $this->assertEquals($result, $expectedValue);
+ }
+
+ /**
+ * Data for method testGetErrorMessage
+ *
+ * @return array
+ */
+ public function errorMessageData()
+ {
+ return [
+ [
+ ['errorCode' => 5, 'errorMessage' => 'Some error message'],
+ 'Some error message'
+ ],
+ [
+ ['errorCode' => 5],
+ null
+ ],
+ ];
+ }
+
+ /**
+ * Test for method getRowNumber
+ *
+ * @dataProvider rowNumberData
+ */
+ public function testGetRowNumber($data, $expectedValue)
+ {
+ $this->testInit($data);
+ $result = $this->model->getRowNumber();
+ $this->assertEquals($result, $expectedValue);
+ }
+
+ /**
+ * Data for method testGetRowNumber
+ *
+ * @return array
+ */
+ public function rowNumberData()
+ {
+ return [
+ [
+ ['errorCode' => 5, 'errorMessage' => 'Some error message', 'rowNumber' => 43],
+ 43
+ ],
+ [
+ ['errorCode' => 5],
+ null
+ ],
+ ];
+ }
+
+ /**
+ * Test for method getColumnName
+ *
+ * @dataProvider columnNameData
+ */
+ public function testGetColumnName($data, $expectedValue)
+ {
+ $this->testInit($data);
+ $result = $this->model->getColumnName();
+ $this->assertEquals($result, $expectedValue);
+ }
+
+ /**
+ * Data for method testGetColumnName
+ *
+ * @return array
+ */
+ public function columnNameData()
+ {
+ return [
+ [
+ [
+ 'errorCode' => 5,
+ 'errorMessage' => 'Some error message',
+ 'rowNumber' => 43,
+ 'columnName' => 'Some column name'
+ ],
+ 'Some column name'
+ ],
+ [
+ ['errorCode' => 5],
+ null
+ ],
+ ];
+ }
+
+ /**
+ * Test for method getErrorLevel
+ *
+ * @dataProvider errorLevelData
+ */
+ public function testGetErrorLevel($data, $expectedValue)
+ {
+ $this->testInit($data);
+ $result = $this->model->getErrorLevel();
+ $this->assertEquals($result, $expectedValue);
+ }
+
+ /**
+ * Data for method testGetErrorLevel
+ *
+ * @return array
+ */
+ public function errorLevelData()
+ {
+ return [
+ [
+ [
+ 'errorCode' => 5,
+ 'errorMessage' => 'Some error message',
+ 'rowNumber' => 43,
+ 'columnName' => 'Some column name',
+ 'errorLevel' => 'critical'
+ ],
+ 'critical'
+ ],
+ [
+ ['errorCode' => 5],
+ null
+ ],
+ ];
+ }
+
+ /**
+ * Test for method getErrorDescription
+ *
+ * @dataProvider errorDescriptionData
+ */
+ public function testGetErrorDescription($data, $expectedValue)
+ {
+ $this->testInit($data);
+ $result = $this->model->getErrorDescription();
+ $this->assertEquals($result, $expectedValue);
+ }
+
+ /**
+ * Data for method testGetErrorDescription
+ *
+ * @return array
+ */
+ public function errorDescriptionData()
+ {
+ return [
+ [
+ [
+ 'errorCode' => 5,
+ 'errorMessage' => 'Some error message',
+ 'rowNumber' => 43,
+ 'columnName' => 'Some column name',
+ 'errorLevel' => 'critical',
+ 'errorDescription' => 'Some error description'
+ ],
+ 'Some error description'
+ ],
+ [
+ ['errorCode' => 5],
+ null
+ ],
+ ];
+ }
+}
diff --git a/app/code/Magento/ImportExport/Test/Unit/Model/Import/Source/CsvTest.php b/app/code/Magento/ImportExport/Test/Unit/Model/Import/Source/CsvTest.php
index 25e6e1a441c08..015809a8d9620 100644
--- a/app/code/Magento/ImportExport/Test/Unit/Model/Import/Source/CsvTest.php
+++ b/app/code/Magento/ImportExport/Test/Unit/Model/Import/Source/CsvTest.php
@@ -111,6 +111,10 @@ public function optionalArgsDataProvider()
];
}
+ /**
+ * @expectedException \InvalidArgumentException
+ * @expectedExceptionMessage wrongColumnsNumber
+ */
public function testRewind()
{
$this->_directoryMock->expects(
@@ -139,6 +143,6 @@ public function testRewind()
$model->next();
$model->next();
$this->assertSame(2, $model->key());
- $this->assertSame(['column1' => '5', 'column2' => ''], $model->current());
+ $model->current();
}
}
diff --git a/app/code/Magento/ImportExport/Test/Unit/Model/Import/SourceAbstractTest.php b/app/code/Magento/ImportExport/Test/Unit/Model/Import/SourceAbstractTest.php
index 225d63b42a06f..ac17ff543661a 100644
--- a/app/code/Magento/ImportExport/Test/Unit/Model/Import/SourceAbstractTest.php
+++ b/app/code/Magento/ImportExport/Test/Unit/Model/Import/SourceAbstractTest.php
@@ -43,9 +43,12 @@ public function testGetColNames()
$this->assertSame(['key1', 'key2', 'key3'], $this->_model->getColNames());
}
+ /**
+ * @expectedException \InvalidArgumentException
+ * @expectedExceptionMessage wrongColumnsNumber
+ */
public function testIteratorInterface()
{
- $this->assertSame(['key1' => '', 'key2' => '', 'key3' => ''], $this->_model->current());
$this->assertSame(-1, $this->_model->key());
$this->assertFalse($this->_model->valid());
@@ -54,7 +57,7 @@ public function testIteratorInterface()
)->method(
'_getNextRow'
)->will(
- $this->onConsecutiveCalls([1, 2, 3], [4, 5], [6, 7, 8], false)
+ $this->onConsecutiveCalls([1, 2, 3], [4, 5, 5], [6, 7, 8])
);
$data = [];
foreach ($this->_model as $key => $value) {
@@ -63,11 +66,12 @@ public function testIteratorInterface()
$this->assertSame(
[
['key1' => 1, 'key2' => 2, 'key3' => 3],
- ['key1' => 4, 'key2' => 5, 'key3' => ''],
+ ['key1' => 4, 'key2' => 5, 'key3' => 5],
['key1' => 6, 'key2' => 7, 'key3' => 8],
],
$data
);
+ $this->_model->current();
}
public function testSeekableInterface()
@@ -81,12 +85,12 @@ public function testSeekableInterface()
)->method(
'_getNextRow'
)->will(
- $this->onConsecutiveCalls([1, 2, 3], [4, 5], [6, 7, 8], [1, 2, 3], [4, 5])
+ $this->onConsecutiveCalls([1, 2, 3], [4, 5, 5], [6, 7, 8], [1, 2, 3], [4, 5, 5])
);
$this->_model->seek(2);
$this->assertSame(['key1' => 6, 'key2' => 7, 'key3' => 8], $this->_model->current());
$this->_model->seek(1);
- $this->assertSame(['key1' => 4, 'key2' => 5, 'key3' => ''], $this->_model->current());
+ $this->assertSame(['key1' => 4, 'key2' => 5, 'key3' => 5], $this->_model->current());
}
/**
diff --git a/app/code/Magento/ImportExport/Test/Unit/Model/ImportTest.php b/app/code/Magento/ImportExport/Test/Unit/Model/ImportTest.php
index 66b249b836222..9e65d5516c612 100644
--- a/app/code/Magento/ImportExport/Test/Unit/Model/ImportTest.php
+++ b/app/code/Magento/ImportExport/Test/Unit/Model/ImportTest.php
@@ -11,7 +11,7 @@
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
* @SuppressWarnings(PHPMD.TooManyFields)
*/
-class ImportTest extends \PHPUnit_Framework_TestCase
+class ImportTest extends \Magento\ImportExport\Test\Unit\Model\Import\AbstractImportTestCase
{
/**
@@ -110,6 +110,8 @@ class ImportTest extends \PHPUnit_Framework_TestCase
*/
public function setUp()
{
+ parent::setUp();
+
$logger = $this->getMockBuilder('\Psr\Log\LoggerInterface')
->disableOriginalConstructor()
->getMock();
@@ -126,16 +128,18 @@ public function setUp()
->disableOriginalConstructor()
->setMethods(['getEntityTypeCode', 'getBehavior', 'getEntities'])
->getMockForAbstractClass();
- $this->_entityFactory = $this->getMock(
- '\Magento\ImportExport\Model\Import\Entity\Factory',
- ['create', 'isNeedToLogInHistory'],
- [],
- '',
- false
- );
- $this->getMockBuilder('\Magento\ImportExport\Model\Import\Entity\Factory')
+ $this->_entityFactory = $this->getMockBuilder('\Magento\ImportExport\Model\Import\Entity\Factory')
->disableOriginalConstructor()
->getMock();
+ $this->_entityAdapter = $this->getMockBuilder('\Magento\ImportExport\Model\Import\Entity\AbstractEntity')
+ ->disableOriginalConstructor()
+ ->setMethods(['importData', '_saveValidatedBunches', 'getErrorAggregator'])
+ ->getMockForAbstractClass();
+ $this->_entityAdapter->method('getErrorAggregator')->willReturn(
+ $this->getErrorAggregatorObject(['initValidationStrategy'])
+ );
+ $this->_entityFactory->method('create')->willReturn($this->_entityAdapter);
+
$this->_importData = $this->getMockBuilder('\Magento\ImportExport\Model\Resource\Import\Data')
->disableOriginalConstructor()
->getMock();
@@ -195,22 +199,17 @@ public function setUp()
])
->setMethods([
'getDataSourceModel',
- '_getEntityAdapter',
'setData',
'getProcessedEntitiesCount',
'getProcessedRowsCount',
- 'getInvalidRowsCount',
- 'getErrorsCount',
'getEntity',
'getBehavior',
'isReportEntityType',
+ '_getEntityAdapter'
])
->getMock();
$this->setPropertyValue($this->import, '_varDirectory', $this->_varDirectory);
- $this->_entityAdapter = $this->getMockBuilder('\Magento\ImportExport\Model\Import\Entity\AbstractEntity')
- ->disableOriginalConstructor()
- ->setMethods(['importData'])
- ->getMockForAbstractClass();
+
}
/**
@@ -238,34 +237,36 @@ public function testImportSource()
$this->import->expects($this->any())
->method('addLogComment')
->with($this->isInstanceOf($phraseClass));
- $this->_entityAdapter->expects($this->once())
+ $this->_entityAdapter->expects($this->any())
->method('importData')
->will($this->returnSelf());
- $this->import->expects($this->once())
+ $this->import->expects($this->any())
->method('_getEntityAdapter')
->will($this->returnValue($this->_entityAdapter));
-
+ $this->_importConfig
+ ->expects($this->any())
+ ->method('getEntities')
+ ->willReturn(
+ [
+ $entityTypeCode => [
+ 'model' => $entityTypeCode
+ ]
+ ]
+ );
$importOnceMethodsReturnNull = [
- 'getEntity',
- 'getBehavior',
- 'getProcessedRowsCount',
- 'getProcessedEntitiesCount',
- 'getInvalidRowsCount',
- 'getErrorsCount',
+ 'getBehavior'
];
foreach ($importOnceMethodsReturnNull as $method) {
$this->import->expects($this->once())->method($method)->will($this->returnValue(null));
}
- $this->import->importSource();
+ $this->assertEquals(true, $this->import->importSource());
}
/**
* Test importSource with expected exception
*
- * @expectedException \Magento\Framework\Exception\AlreadyExistsException
- * @expectedExceptionMessage URL key for specified store already exists.
*/
public function testImportSourceException()
{
@@ -277,7 +278,7 @@ public function testImportSourceException()
->method('getEntityTypeCode')
->will($this->returnValue($entityTypeCode));
$behaviour = 'behaviour';
- $this->_importData->expects($this->once())
+ $this->_importData->expects($this->any())
->method('getBehavior')
->will($this->returnValue($behaviour));
$this->import->expects($this->any())
@@ -291,14 +292,13 @@ public function testImportSourceException()
$this->import->expects($this->any())
->method('addLogComment')
->with($this->isInstanceOf($phraseClass));
- $this->_entityAdapter->expects($this->once())
+ $this->_entityAdapter->expects($this->any())
->method('importData')
->will($this->throwException($exceptionMock));
- $this->import->expects($this->once())
+ $this->import->expects($this->any())
->method('_getEntityAdapter')
->will($this->returnValue($this->_entityAdapter));
$importOnceMethodsReturnNull = [
- 'getEntity',
'getBehavior',
];
diff --git a/app/code/Magento/ImportExport/Test/Unit/Model/Report/CsvTest.php b/app/code/Magento/ImportExport/Test/Unit/Model/Report/CsvTest.php
new file mode 100644
index 0000000000000..ed812fd387504
--- /dev/null
+++ b/app/code/Magento/ImportExport/Test/Unit/Model/Report/CsvTest.php
@@ -0,0 +1,114 @@
+reportHelperMock = $this->getMock('\Magento\ImportExport\Helper\Report', [], [], '', false);
+
+ $this->outputCsvFactoryMock = $this->getMock(
+ '\Magento\ImportExport\Model\Export\Adapter\CsvFactory',
+ ['create'],
+ [],
+ '',
+ false
+ );
+ $this->outputCsvMock = $this->getMock('\Magento\ImportExport\Model\Export\Adapter\Csv', [], [], '', false);
+ $this->outputCsvFactoryMock->expects($this->any())->method('create')->willReturn($this->outputCsvMock);
+
+ $this->sourceCsvFactoryMock = $this->getMock(
+ '\Magento\ImportExport\Model\Import\Source\CsvFactory',
+ ['create'],
+ [],
+ '',
+ false
+ );
+ $this->sourceCsvMock = $this->getMock('\Magento\ImportExport\Model\Import\Source\Csv', [], [], '', false);
+ $this->sourceCsvMock->expects($this->any())->method('valid')->willReturnOnConsecutiveCalls(true, true, false);
+ $this->sourceCsvMock->expects($this->any())->method('current')->willReturnOnConsecutiveCalls(
+ [23 => 'first error'],
+ [27 => 'second error']
+ );
+ $this->sourceCsvFactoryMock->expects($this->any())->method('create')->willReturn($this->sourceCsvMock);
+
+ $this->filesystemMock = $this->getMock('\Magento\Framework\Filesystem', [], [], '', false);
+
+
+ $this->csvModel = $objectManager->getObject(
+ '\Magento\ImportExport\Model\Report\Csv',
+ [
+ 'reportHelper' => $this->reportHelperMock,
+ 'sourceCsvFactory' => $this->sourceCsvFactoryMock,
+ 'outputCsvFactory' => $this->outputCsvFactoryMock,
+ 'filesystem' => $this->filesystemMock
+ ]
+ );
+ }
+
+ public function testCreateReport()
+ {
+ $errorAggregatorMock = $this->getMock(
+ 'Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingErrorAggregator',
+ [],
+ [],
+ '',
+ false
+ );
+ $errorProcessingMock = $this->getMock(
+ 'Magento\ImportExport\Model\Import\ErrorProcessing',
+ ['getErrorMessage'],
+ [],
+ '',
+ false
+ );
+ $errorProcessingMock->expects($this->any())->method('getErrorMessage')->willReturn('some_error_message');
+ $errorAggregatorMock->expects($this->any())->method('getErrorByRowNumber')->willReturn([$errorProcessingMock]);
+ $this->sourceCsvMock->expects($this->any())->method('getColNames')->willReturn([]);
+
+ $name = $this->csvModel->createReport('some_file_name', $errorAggregatorMock, true);
+
+ $this->assertEquals($name, 'some_file_name_error_report.csv');
+ }
+}
diff --git a/app/code/Magento/ImportExport/etc/di.xml b/app/code/Magento/ImportExport/etc/di.xml
index bf332bd3080a3..88e5915a35a2e 100644
--- a/app/code/Magento/ImportExport/etc/di.xml
+++ b/app/code/Magento/ImportExport/etc/di.xml
@@ -8,6 +8,8 @@
+
+
diff --git a/app/code/Magento/ImportExport/etc/module.xml b/app/code/Magento/ImportExport/etc/module.xml
index 4dad36db35038..e072148a1dd4b 100644
--- a/app/code/Magento/ImportExport/etc/module.xml
+++ b/app/code/Magento/ImportExport/etc/module.xml
@@ -6,6 +6,6 @@
*/
-->
-
+
diff --git a/app/code/Magento/ImportExport/view/adminhtml/layout/adminhtml_history_grid_block.xml b/app/code/Magento/ImportExport/view/adminhtml/layout/adminhtml_history_grid_block.xml
index 839fdb5f7ecb9..22df8f655b3da 100644
--- a/app/code/Magento/ImportExport/view/adminhtml/layout/adminhtml_history_grid_block.xml
+++ b/app/code/Magento/ImportExport/view/adminhtml/layout/adminhtml_history_grid_block.xml
@@ -54,6 +54,18 @@
Magento\ImportExport\Block\Adminhtml\Grid\Column\Renderer\Download
+
+
+ Error File
+ type
+ 0
+ 0
+ center
+ action
+ Magento\ImportExport\Block\Adminhtml\Grid\Column\Renderer\Error
+
+
+
Execution Time
diff --git a/app/code/Magento/ImportExport/view/adminhtml/layout/adminhtml_import_index.xml b/app/code/Magento/ImportExport/view/adminhtml/layout/adminhtml_import_index.xml
index ba7694ea26659..2168ec3cd3416 100644
--- a/app/code/Magento/ImportExport/view/adminhtml/layout/adminhtml_import_index.xml
+++ b/app/code/Magento/ImportExport/view/adminhtml/layout/adminhtml_import_index.xml
@@ -6,6 +6,9 @@
*/
-->
+
+
+
diff --git a/app/code/Magento/ImportExport/view/adminhtml/templates/import/form/before.phtml b/app/code/Magento/ImportExport/view/adminhtml/templates/import/form/before.phtml
index c7993391938a7..ec4d7ec59ed64 100644
--- a/app/code/Magento/ImportExport/view/adminhtml/templates/import/form/before.phtml
+++ b/app/code/Magento/ImportExport/view/adminhtml/templates/import/form/before.phtml
@@ -31,6 +31,12 @@ require(['jquery', 'prototype'], function(jQuery){
*/
entityBehaviors: getEntityBehaviors() ?>,
+ /**
+ * Behaviour notes for import entities
+ * @type {Array}
+ */
+ entityBehaviorsNotes: getEntityBehaviorsNotes() ?>,
+
/**
* Base url
* @type {string}
@@ -129,6 +135,25 @@ require(['jquery', 'prototype'], function(jQuery){
this.showUploadFile(false);
this.showSampleFile(false);
}
+ this.handleImportBehaviorSelector();
+ },
+
+ /**
+ * Handle value change in behavior selector
+ */
+ handleImportBehaviorSelector: function() {
+ var entity = jQuery('#entity');
+ if (entity && entity.val()) {
+ var notes = this.entityBehaviorsNotes[entity.val()];
+ var requiredBehavior = this.entityBehaviors[entity.val()];
+ var behaviorInput = jQuery('#' + requiredBehavior);
+ var behavior = behaviorInput && behaviorInput.val();
+ if (behavior && notes[behavior]) {
+ jQuery('#' + requiredBehavior + '-note').html(notes[behavior]);
+ } else {
+ jQuery('#' + requiredBehavior + '-note').html('');
+ }
+ }
},
/**
diff --git a/app/code/Magento/ImportExport/view/adminhtml/web/css/importexport.css b/app/code/Magento/ImportExport/view/adminhtml/web/css/importexport.css
new file mode 100644
index 0000000000000..968e2b74fba06
--- /dev/null
+++ b/app/code/Magento/ImportExport/view/adminhtml/web/css/importexport.css
@@ -0,0 +1,15 @@
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+.import-error-wrapper {
+ margin-top: 10px;
+}
+
+.import-error-list {
+ max-height: 120px;
+ overflow-y: auto;
+ padding: 10px;
+ background-color: #fcc;
+ margin: 10px 0 0 0;
+}
diff --git a/composer.json b/composer.json
index 3d56ffc30ad81..a8a75f38b5e30 100644
--- a/composer.json
+++ b/composer.json
@@ -109,6 +109,7 @@
"magento/module-dhl": "self.version",
"magento/module-directory": "self.version",
"magento/module-downloadable": "self.version",
+ "magento/module-downloadable-import-export": "self.version",
"magento/module-eav": "self.version",
"magento/module-email": "self.version",
"magento/module-encryption-key": "self.version",
diff --git a/composer.lock b/composer.lock
index 92f5ac69ee2ed..628b9af4df68e 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
- "hash": "3c94391144a4c2de663dfa465b3a2e8a",
+ "hash": "de0d450a2c86bda5a2101109be73daff",
"packages": [
{
"name": "braintree/braintree_php",
@@ -121,16 +121,16 @@
},
{
"name": "justinrainbow/json-schema",
- "version": "1.4.4",
+ "version": "1.5.0",
"source": {
"type": "git",
"url": "https://github.com/justinrainbow/json-schema.git",
- "reference": "8dc9b9d85ab639ca60ab4608b34c1279d6ae7bce"
+ "reference": "a4bee9f4b344b66e0a0d96c7afae1e92edf385fe"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/8dc9b9d85ab639ca60ab4608b34c1279d6ae7bce",
- "reference": "8dc9b9d85ab639ca60ab4608b34c1279d6ae7bce",
+ "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/a4bee9f4b344b66e0a0d96c7afae1e92edf385fe",
+ "reference": "a4bee9f4b344b66e0a0d96c7afae1e92edf385fe",
"shasum": ""
},
"require": {
@@ -151,8 +151,8 @@
}
},
"autoload": {
- "psr-0": {
- "JsonSchema": "src/"
+ "psr-4": {
+ "JsonSchema\\": "src/JsonSchema/"
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -183,7 +183,7 @@
"json",
"schema"
],
- "time": "2015-07-14 16:29:50"
+ "time": "2015-09-08 22:28:04"
},
{
"name": "magento/composer",
@@ -335,7 +335,7 @@
"ZF1",
"framework"
],
- "time": "2015-06-02 08:04:41"
+ "time": "2015-02-06 17:25:45"
},
{
"name": "monolog/monolog",
@@ -766,16 +766,16 @@
},
{
"name": "symfony/finder",
- "version": "v2.7.3",
+ "version": "v2.7.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/Finder.git",
- "reference": "ae0f363277485094edc04c9f3cbe595b183b78e4"
+ "reference": "fff4b0c362640a0ab7355e2647b3d461608e9065"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/Finder/zipball/ae0f363277485094edc04c9f3cbe595b183b78e4",
- "reference": "ae0f363277485094edc04c9f3cbe595b183b78e4",
+ "url": "https://api.github.com/repos/symfony/Finder/zipball/fff4b0c362640a0ab7355e2647b3d461608e9065",
+ "reference": "fff4b0c362640a0ab7355e2647b3d461608e9065",
"shasum": ""
},
"require": {
@@ -811,20 +811,20 @@
],
"description": "Symfony Finder Component",
"homepage": "https://symfony.com",
- "time": "2015-07-09 16:07:40"
+ "time": "2015-08-26 17:56:37"
},
{
"name": "symfony/process",
- "version": "v2.7.3",
+ "version": "v2.7.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/Process.git",
- "reference": "48aeb0e48600321c272955132d7606ab0a49adb3"
+ "reference": "f7b3f73f70a7f8f49a1c838dc3debbf054732d8e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/Process/zipball/48aeb0e48600321c272955132d7606ab0a49adb3",
- "reference": "48aeb0e48600321c272955132d7606ab0a49adb3",
+ "url": "https://api.github.com/repos/symfony/Process/zipball/f7b3f73f70a7f8f49a1c838dc3debbf054732d8e",
+ "reference": "f7b3f73f70a7f8f49a1c838dc3debbf054732d8e",
"shasum": ""
},
"require": {
@@ -860,7 +860,7 @@
],
"description": "Symfony Process Component",
"homepage": "https://symfony.com",
- "time": "2015-07-01 11:25:50"
+ "time": "2015-08-27 06:45:45"
},
{
"name": "tedivm/jshrink",
@@ -958,12 +958,12 @@
"source": {
"type": "git",
"url": "https://github.com/zendframework/zend-code.git",
- "reference": "0ed94f842ba60cdc900c46a61bdbd7ac95a3e140"
+ "reference": "cfd5951ff4348e4430850560416c7ddb755f95d3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/zendframework/zend-code/zipball/0ed94f842ba60cdc900c46a61bdbd7ac95a3e140",
- "reference": "0ed94f842ba60cdc900c46a61bdbd7ac95a3e140",
+ "reference": "cfd5951ff4348e4430850560416c7ddb755f95d3",
"shasum": ""
},
"require": {
@@ -972,9 +972,6 @@
},
"require-dev": {
"doctrine/common": ">=2.1",
- "fabpot/php-cs-fixer": "1.7.*",
- "phpunit/phpunit": "~4.0",
- "satooshi/php-coveralls": "dev-master",
"zendframework/zend-stdlib": "self.version"
},
"suggest": {
@@ -990,7 +987,7 @@
},
"autoload": {
"psr-4": {
- "Zend\\Code\\": "src/"
+ "Zend\\Code\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -998,12 +995,12 @@
"BSD-3-Clause"
],
"description": "provides facilities to generate arbitrary code using an object oriented interface",
- "homepage": "https://github.com/zendframework/zend-code",
+ "homepage": "https://github.com/zendframework/zf2",
"keywords": [
"code",
"zf2"
],
- "time": "2015-03-31 15:39:14"
+ "time": "2015-04-01 17:59:08"
},
{
"name": "zendframework/zend-config",
@@ -1011,12 +1008,12 @@
"source": {
"type": "git",
"url": "https://github.com/zendframework/zend-config.git",
- "reference": "95f3a4b3fa85d49e6f060183122de4596fa6d29d"
+ "reference": "8682fe4e2923b383bb6472fc84b5796a07589163"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/zendframework/zend-config/zipball/95f3a4b3fa85d49e6f060183122de4596fa6d29d",
- "reference": "95f3a4b3fa85d49e6f060183122de4596fa6d29d",
+ "reference": "8682fe4e2923b383bb6472fc84b5796a07589163",
"shasum": ""
},
"require": {
@@ -1024,9 +1021,6 @@
"zendframework/zend-stdlib": "self.version"
},
"require-dev": {
- "fabpot/php-cs-fixer": "1.7.*",
- "phpunit/phpunit": "~4.0",
- "satooshi/php-coveralls": "dev-master",
"zendframework/zend-filter": "self.version",
"zendframework/zend-i18n": "self.version",
"zendframework/zend-json": "self.version",
@@ -1047,7 +1041,7 @@
},
"autoload": {
"psr-4": {
- "Zend\\Config\\": "src/"
+ "Zend\\Config\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -1055,12 +1049,12 @@
"BSD-3-Clause"
],
"description": "provides a nested object property based user interface for accessing this configuration data within application code",
- "homepage": "https://github.com/zendframework/zend-config",
+ "homepage": "https://github.com/zendframework/zf2",
"keywords": [
"config",
"zf2"
],
- "time": "2015-03-25 20:55:48"
+ "time": "2015-04-01 17:59:31"
},
{
"name": "zendframework/zend-console",
@@ -1068,23 +1062,18 @@
"source": {
"type": "git",
"url": "https://github.com/zendframework/zend-console.git",
- "reference": "54823d9ba6f8ce39046384ee5a043b5b3d5f56d7"
+ "reference": "94ab6663b07e19f20b3319ecf317bd72b6a72dca"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/zendframework/zend-console/zipball/54823d9ba6f8ce39046384ee5a043b5b3d5f56d7",
- "reference": "54823d9ba6f8ce39046384ee5a043b5b3d5f56d7",
+ "reference": "94ab6663b07e19f20b3319ecf317bd72b6a72dca",
"shasum": ""
},
"require": {
"php": ">=5.3.23",
"zendframework/zend-stdlib": "self.version"
},
- "require-dev": {
- "fabpot/php-cs-fixer": "1.7.*",
- "phpunit/phpunit": "~4.0",
- "satooshi/php-coveralls": "dev-master"
- },
"suggest": {
"zendframework/zend-filter": "To support DefaultRouteMatcher usage",
"zendframework/zend-validator": "To support DefaultRouteMatcher usage"
@@ -1098,19 +1087,19 @@
},
"autoload": {
"psr-4": {
- "Zend\\Console\\": "src/"
+ "Zend\\Console\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
- "homepage": "https://github.com/zendframework/zend-console",
+ "homepage": "https://github.com/zendframework/zf2",
"keywords": [
"console",
"zf2"
],
- "time": "2015-03-25 20:55:48"
+ "time": "2015-04-01 17:59:48"
},
{
"name": "zendframework/zend-crypt",
@@ -1169,12 +1158,12 @@
"source": {
"type": "git",
"url": "https://github.com/zendframework/zend-di.git",
- "reference": "b9f8de081adecf71a003a569e9ba76c0a4c00bf2"
+ "reference": "0811f2a67ad0b50dfb8d602ed67cde0b82249190"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/zendframework/zend-di/zipball/b9f8de081adecf71a003a569e9ba76c0a4c00bf2",
- "reference": "b9f8de081adecf71a003a569e9ba76c0a4c00bf2",
+ "reference": "0811f2a67ad0b50dfb8d602ed67cde0b82249190",
"shasum": ""
},
"require": {
@@ -1183,9 +1172,6 @@
"zendframework/zend-stdlib": "self.version"
},
"require-dev": {
- "fabpot/php-cs-fixer": "1.7.*",
- "phpunit/phpunit": "~4.0",
- "satooshi/php-coveralls": "dev-master",
"zendframework/zend-servicemanager": "self.version"
},
"suggest": {
@@ -1200,19 +1186,19 @@
},
"autoload": {
"psr-4": {
- "Zend\\Di\\": "src/"
+ "Zend\\Di\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
- "homepage": "https://github.com/zendframework/zend-di",
+ "homepage": "https://github.com/zendframework/zf2",
"keywords": [
"di",
"zf2"
],
- "time": "2015-03-25 20:55:48"
+ "time": "2015-04-01 18:01:30"
},
{
"name": "zendframework/zend-escaper",
@@ -1220,22 +1206,17 @@
"source": {
"type": "git",
"url": "https://github.com/zendframework/zend-escaper.git",
- "reference": "15e5769e4fcdb4bf07ebd76500810e7070e23a97"
+ "reference": "65b3328627362b0be1d5e9067bc846511d1fbc96"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/zendframework/zend-escaper/zipball/15e5769e4fcdb4bf07ebd76500810e7070e23a97",
- "reference": "15e5769e4fcdb4bf07ebd76500810e7070e23a97",
+ "reference": "65b3328627362b0be1d5e9067bc846511d1fbc96",
"shasum": ""
},
"require": {
"php": ">=5.3.23"
},
- "require-dev": {
- "fabpot/php-cs-fixer": "1.7.*",
- "phpunit/phpunit": "~4.0",
- "satooshi/php-coveralls": "dev-master"
- },
"type": "library",
"extra": {
"branch-alias": {
@@ -1245,19 +1226,19 @@
},
"autoload": {
"psr-4": {
- "Zend\\Escaper\\": "src/"
+ "Zend\\Escaper\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
- "homepage": "https://github.com/zendframework/zend-escaper",
+ "homepage": "https://github.com/zendframework/zf2",
"keywords": [
"escaper",
"zf2"
],
- "time": "2015-03-23 18:29:14"
+ "time": "2015-04-01 18:02:07"
},
{
"name": "zendframework/zend-eventmanager",
@@ -1265,23 +1246,18 @@
"source": {
"type": "git",
"url": "https://github.com/zendframework/zend-eventmanager.git",
- "reference": "58d21c95c7005a527262fd536499195f104e83f9"
+ "reference": "38df5b567d4ff4d22144745c503ba0502d0d5695"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/zendframework/zend-eventmanager/zipball/58d21c95c7005a527262fd536499195f104e83f9",
- "reference": "58d21c95c7005a527262fd536499195f104e83f9",
+ "reference": "38df5b567d4ff4d22144745c503ba0502d0d5695",
"shasum": ""
},
"require": {
"php": ">=5.3.23",
"zendframework/zend-stdlib": "self.version"
},
- "require-dev": {
- "fabpot/php-cs-fixer": "1.7.*",
- "phpunit/phpunit": "~4.0",
- "satooshi/php-coveralls": "dev-master"
- },
"type": "library",
"extra": {
"branch-alias": {
@@ -1291,19 +1267,19 @@
},
"autoload": {
"psr-4": {
- "Zend\\EventManager\\": "src/"
+ "Zend\\EventManager\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
- "homepage": "https://github.com/zendframework/zend-event-manager",
+ "homepage": "https://github.com/zendframework/zf2",
"keywords": [
"eventmanager",
"zf2"
],
- "time": "2015-03-23 18:29:14"
+ "time": "2015-04-01 18:05:26"
},
{
"name": "zendframework/zend-filter",
@@ -1311,12 +1287,12 @@
"source": {
"type": "git",
"url": "https://github.com/zendframework/zend-filter.git",
- "reference": "6d8aed2da81b62a04747346c4370562cdbe34595"
+ "reference": "b13741a88553351fc52472de529b57b580b8f6f1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/zendframework/zend-filter/zipball/6d8aed2da81b62a04747346c4370562cdbe34595",
- "reference": "6d8aed2da81b62a04747346c4370562cdbe34595",
+ "reference": "b13741a88553351fc52472de529b57b580b8f6f1",
"shasum": ""
},
"require": {
@@ -1324,9 +1300,6 @@
"zendframework/zend-stdlib": "self.version"
},
"require-dev": {
- "fabpot/php-cs-fixer": "1.7.*",
- "phpunit/phpunit": "~4.0",
- "satooshi/php-coveralls": "dev-master",
"zendframework/zend-crypt": "self.version",
"zendframework/zend-servicemanager": "self.version",
"zendframework/zend-uri": "self.version"
@@ -1346,7 +1319,7 @@
},
"autoload": {
"psr-4": {
- "Zend\\Filter\\": "src/"
+ "Zend\\Filter\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -1354,12 +1327,12 @@
"BSD-3-Clause"
],
"description": "provides a set of commonly needed data filters",
- "homepage": "https://github.com/zendframework/zend-filter",
+ "homepage": "https://github.com/zendframework/zf2",
"keywords": [
"filter",
"zf2"
],
- "time": "2015-03-25 20:55:48"
+ "time": "2015-04-01 18:09:25"
},
{
"name": "zendframework/zend-form",
@@ -1367,12 +1340,12 @@
"source": {
"type": "git",
"url": "https://github.com/zendframework/zend-form.git",
- "reference": "bca0db55718355d25c2c10fdd41a83561f1c94b3"
+ "reference": "09f5bd46ffbf783df22281898e2175b291bd43a3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/zendframework/zend-form/zipball/bca0db55718355d25c2c10fdd41a83561f1c94b3",
- "reference": "bca0db55718355d25c2c10fdd41a83561f1c94b3",
+ "reference": "09f5bd46ffbf783df22281898e2175b291bd43a3",
"shasum": ""
},
"require": {
@@ -1381,9 +1354,6 @@
"zendframework/zend-stdlib": "self.version"
},
"require-dev": {
- "fabpot/php-cs-fixer": "1.7.*",
- "phpunit/phpunit": "~4.0",
- "satooshi/php-coveralls": "dev-master",
"zendframework/zend-captcha": "self.version",
"zendframework/zend-code": "self.version",
"zendframework/zend-eventmanager": "self.version",
@@ -1414,19 +1384,19 @@
},
"autoload": {
"psr-4": {
- "Zend\\Form\\": "src/"
+ "Zend\\Form\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
- "homepage": "https://github.com/zendframework/zend-form",
+ "homepage": "https://github.com/zendframework/zf2",
"keywords": [
"form",
"zf2"
],
- "time": "2015-03-28 20:29:18"
+ "time": "2015-04-01 18:09:25"
},
{
"name": "zendframework/zend-http",
@@ -1434,12 +1404,12 @@
"source": {
"type": "git",
"url": "https://github.com/zendframework/zend-http.git",
- "reference": "9c6047a0bdb3094d3ea07a215ff929cc47de4deb"
+ "reference": "ee6220609845b32d1b2873c9ac694aef56d508f5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/zendframework/zend-http/zipball/9c6047a0bdb3094d3ea07a215ff929cc47de4deb",
- "reference": "9c6047a0bdb3094d3ea07a215ff929cc47de4deb",
+ "reference": "ee6220609845b32d1b2873c9ac694aef56d508f5",
"shasum": ""
},
"require": {
@@ -1449,11 +1419,6 @@
"zendframework/zend-uri": "self.version",
"zendframework/zend-validator": "self.version"
},
- "require-dev": {
- "fabpot/php-cs-fixer": "1.7.*",
- "phpunit/phpunit": "~4.0",
- "satooshi/php-coveralls": "dev-master"
- },
"type": "library",
"extra": {
"branch-alias": {
@@ -1463,7 +1428,7 @@
},
"autoload": {
"psr-4": {
- "Zend\\Http\\": "src/"
+ "Zend\\Http\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -1471,12 +1436,12 @@
"BSD-3-Clause"
],
"description": "provides an easy interface for performing Hyper-Text Transfer Protocol (HTTP) requests",
- "homepage": "https://github.com/zendframework/zend-http",
+ "homepage": "https://github.com/zendframework/zf2",
"keywords": [
"http",
"zf2"
],
- "time": "2015-03-27 15:46:30"
+ "time": "2015-04-01 18:09:25"
},
{
"name": "zendframework/zend-i18n",
@@ -1484,12 +1449,12 @@
"source": {
"type": "git",
"url": "https://github.com/zendframework/zend-i18n.git",
- "reference": "9aebc5287373a802540d75fe5508417f866c2e52"
+ "reference": "33051775d9a8c341fe3b77d1f3daa0e921e2f4bd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/zendframework/zend-i18n/zipball/9aebc5287373a802540d75fe5508417f866c2e52",
- "reference": "9aebc5287373a802540d75fe5508417f866c2e52",
+ "reference": "33051775d9a8c341fe3b77d1f3daa0e921e2f4bd",
"shasum": ""
},
"require": {
@@ -1497,9 +1462,6 @@
"zendframework/zend-stdlib": "self.version"
},
"require-dev": {
- "fabpot/php-cs-fixer": "1.7.*",
- "phpunit/phpunit": "~4.0",
- "satooshi/php-coveralls": "dev-master",
"zendframework/zend-cache": "self.version",
"zendframework/zend-config": "self.version",
"zendframework/zend-eventmanager": "self.version",
@@ -1528,19 +1490,19 @@
},
"autoload": {
"psr-4": {
- "Zend\\I18n\\": "src/"
+ "Zend\\I18n\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
- "homepage": "https://github.com/zendframework/zend-i18n",
+ "homepage": "https://github.com/zendframework/zf2",
"keywords": [
"i18n",
"zf2"
],
- "time": "2015-03-25 20:55:48"
+ "time": "2015-04-01 18:09:26"
},
{
"name": "zendframework/zend-inputfilter",
@@ -1548,12 +1510,12 @@
"source": {
"type": "git",
"url": "https://github.com/zendframework/zend-inputfilter.git",
- "reference": "4b1398f3635fae3cc5e873c5bb067274f3d10a93"
+ "reference": "16856fec61f285e41e5492235220a4dec06ab90f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/zendframework/zend-inputfilter/zipball/4b1398f3635fae3cc5e873c5bb067274f3d10a93",
- "reference": "4b1398f3635fae3cc5e873c5bb067274f3d10a93",
+ "reference": "16856fec61f285e41e5492235220a4dec06ab90f",
"shasum": ""
},
"require": {
@@ -1563,9 +1525,6 @@
"zendframework/zend-validator": "self.version"
},
"require-dev": {
- "fabpot/php-cs-fixer": "1.7.*",
- "phpunit/phpunit": "~4.0",
- "satooshi/php-coveralls": "dev-master",
"zendframework/zend-servicemanager": "self.version"
},
"suggest": {
@@ -1580,19 +1539,19 @@
},
"autoload": {
"psr-4": {
- "Zend\\InputFilter\\": "src/"
+ "Zend\\InputFilter\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
- "homepage": "https://github.com/zendframework/zend-input-filter",
+ "homepage": "https://github.com/zendframework/zf2",
"keywords": [
"inputfilter",
"zf2"
],
- "time": "2015-03-23 18:29:14"
+ "time": "2015-04-01 18:09:26"
},
{
"name": "zendframework/zend-json",
@@ -1600,12 +1559,12 @@
"source": {
"type": "git",
"url": "https://github.com/zendframework/zend-json.git",
- "reference": "2d845e151c1b9a237cf1899ac31e17fb10bd1e3f"
+ "reference": "76aeb27e4baf39799e5ca3cf6f2fdd6748ee930c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/zendframework/zend-json/zipball/2d845e151c1b9a237cf1899ac31e17fb10bd1e3f",
- "reference": "2d845e151c1b9a237cf1899ac31e17fb10bd1e3f",
+ "reference": "76aeb27e4baf39799e5ca3cf6f2fdd6748ee930c",
"shasum": ""
},
"require": {
@@ -1613,9 +1572,6 @@
"zendframework/zend-stdlib": "self.version"
},
"require-dev": {
- "fabpot/php-cs-fixer": "1.7.*",
- "phpunit/phpunit": "~4.0",
- "satooshi/php-coveralls": "dev-master",
"zendframework/zend-http": "self.version",
"zendframework/zend-server": "self.version"
},
@@ -1633,7 +1589,7 @@
},
"autoload": {
"psr-4": {
- "Zend\\Json\\": "src/"
+ "Zend\\Json\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -1641,12 +1597,12 @@
"BSD-3-Clause"
],
"description": "provides convenience methods for serializing native PHP to JSON and decoding JSON to native PHP",
- "homepage": "https://github.com/zendframework/zend-json",
+ "homepage": "https://github.com/zendframework/zf2",
"keywords": [
"json",
"zf2"
],
- "time": "2015-03-25 20:55:48"
+ "time": "2015-04-01 18:09:26"
},
{
"name": "zendframework/zend-loader",
@@ -1654,22 +1610,17 @@
"source": {
"type": "git",
"url": "https://github.com/zendframework/zend-loader.git",
- "reference": "65de2c7a56f8eee633c6bf1cfab73e45648880d4"
+ "reference": "6868b8a0c346f17fb97724c3a63aa2cbf6b94865"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/zendframework/zend-loader/zipball/65de2c7a56f8eee633c6bf1cfab73e45648880d4",
- "reference": "65de2c7a56f8eee633c6bf1cfab73e45648880d4",
+ "reference": "6868b8a0c346f17fb97724c3a63aa2cbf6b94865",
"shasum": ""
},
"require": {
"php": ">=5.3.23"
},
- "require-dev": {
- "fabpot/php-cs-fixer": "1.7.*",
- "phpunit/phpunit": "~4.0",
- "satooshi/php-coveralls": "dev-master"
- },
"type": "library",
"extra": {
"branch-alias": {
@@ -1679,19 +1630,19 @@
},
"autoload": {
"psr-4": {
- "Zend\\Loader\\": "src/"
+ "Zend\\Loader\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
- "homepage": "https://github.com/zendframework/zend-loader",
+ "homepage": "https://github.com/zendframework/zf2",
"keywords": [
"loader",
"zf2"
],
- "time": "2015-03-23 18:29:14"
+ "time": "2015-04-01 18:09:26"
},
{
"name": "zendframework/zend-log",
@@ -1699,12 +1650,12 @@
"source": {
"type": "git",
"url": "https://github.com/zendframework/zend-log.git",
- "reference": "002e3c810cad7e31e51c9895e9e3cb6fbd312cdd"
+ "reference": "2d5d20fd45470506bdaff727c46dc25fe953146e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/zendframework/zend-log/zipball/002e3c810cad7e31e51c9895e9e3cb6fbd312cdd",
- "reference": "002e3c810cad7e31e51c9895e9e3cb6fbd312cdd",
+ "reference": "2d5d20fd45470506bdaff727c46dc25fe953146e",
"shasum": ""
},
"require": {
@@ -1713,9 +1664,6 @@
"zendframework/zend-stdlib": "self.version"
},
"require-dev": {
- "fabpot/php-cs-fixer": "1.7.*",
- "phpunit/phpunit": "~4.0",
- "satooshi/php-coveralls": "dev-master",
"zendframework/zend-console": "self.version",
"zendframework/zend-db": "self.version",
"zendframework/zend-escaper": "self.version",
@@ -1739,7 +1687,7 @@
},
"autoload": {
"psr-4": {
- "Zend\\Log\\": "src/"
+ "Zend\\Log\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -1747,13 +1695,13 @@
"BSD-3-Clause"
],
"description": "component for general purpose logging",
- "homepage": "https://github.com/zendframework/zend-log",
+ "homepage": "https://github.com/zendframework/zf2",
"keywords": [
"log",
"logging",
"zf2"
],
- "time": "2015-03-25 20:55:48"
+ "time": "2015-04-01 18:09:26"
},
{
"name": "zendframework/zend-math",
@@ -1761,22 +1709,17 @@
"source": {
"type": "git",
"url": "https://github.com/zendframework/zend-math.git",
- "reference": "f41fe4acfd809c14f2a802d1aa45dec8fcd2cc73"
+ "reference": "634123f83ca90b6613f132d0d100e6b5e9890a29"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/zendframework/zend-math/zipball/f41fe4acfd809c14f2a802d1aa45dec8fcd2cc73",
- "reference": "f41fe4acfd809c14f2a802d1aa45dec8fcd2cc73",
+ "reference": "634123f83ca90b6613f132d0d100e6b5e9890a29",
"shasum": ""
},
"require": {
"php": ">=5.3.23"
},
- "require-dev": {
- "fabpot/php-cs-fixer": "1.7.*",
- "phpunit/phpunit": "~4.0",
- "satooshi/php-coveralls": "dev-master"
- },
"suggest": {
"ext-bcmath": "If using the bcmath functionality",
"ext-gmp": "If using the gmp functionality",
@@ -1792,19 +1735,19 @@
},
"autoload": {
"psr-4": {
- "Zend\\Math\\": "src/"
+ "Zend\\Math\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
- "homepage": "https://github.com/zendframework/zend-math",
+ "homepage": "https://github.com/zendframework/zf2",
"keywords": [
"math",
"zf2"
],
- "time": "2015-03-23 18:29:14"
+ "time": "2015-04-01 18:09:27"
},
{
"name": "zendframework/zend-modulemanager",
@@ -1812,12 +1755,12 @@
"source": {
"type": "git",
"url": "https://github.com/zendframework/zend-modulemanager.git",
- "reference": "af7ae3cd29a1efb73cc66ae1081e606039d5c20f"
+ "reference": "cbe16b0eafe734a062ed0182381e64b9c953dccf"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/zendframework/zend-modulemanager/zipball/af7ae3cd29a1efb73cc66ae1081e606039d5c20f",
- "reference": "af7ae3cd29a1efb73cc66ae1081e606039d5c20f",
+ "reference": "cbe16b0eafe734a062ed0182381e64b9c953dccf",
"shasum": ""
},
"require": {
@@ -1826,9 +1769,6 @@
"zendframework/zend-stdlib": "self.version"
},
"require-dev": {
- "fabpot/php-cs-fixer": "1.7.*",
- "phpunit/phpunit": "~4.0",
- "satooshi/php-coveralls": "dev-master",
"zendframework/zend-config": "self.version",
"zendframework/zend-console": "self.version",
"zendframework/zend-loader": "self.version",
@@ -1850,19 +1790,19 @@
},
"autoload": {
"psr-4": {
- "Zend\\ModuleManager\\": "src/"
+ "Zend\\ModuleManager\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
- "homepage": "https://github.com/zendframework/zend-module-manager",
+ "homepage": "https://github.com/zendframework/zf2",
"keywords": [
"modulemanager",
"zf2"
],
- "time": "2015-03-23 18:29:14"
+ "time": "2015-04-01 18:09:27"
},
{
"name": "zendframework/zend-mvc",
@@ -1870,12 +1810,12 @@
"source": {
"type": "git",
"url": "https://github.com/zendframework/zend-mvc.git",
- "reference": "0b4a4a829b30be510a3f215c4ff00c703ee8b431"
+ "reference": "bfff0f5f9e4d925ee13b8c159c9d6ae7e0db5412"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/zendframework/zend-mvc/zipball/0b4a4a829b30be510a3f215c4ff00c703ee8b431",
- "reference": "0b4a4a829b30be510a3f215c4ff00c703ee8b431",
+ "reference": "bfff0f5f9e4d925ee13b8c159c9d6ae7e0db5412",
"shasum": ""
},
"require": {
@@ -1886,9 +1826,6 @@
"zendframework/zend-stdlib": "self.version"
},
"require-dev": {
- "fabpot/php-cs-fixer": "1.7.*",
- "phpunit/phpunit": "~4.0",
- "satooshi/php-coveralls": "dev-master",
"zendframework/zend-authentication": "self.version",
"zendframework/zend-console": "self.version",
"zendframework/zend-di": "self.version",
@@ -1937,19 +1874,19 @@
},
"autoload": {
"psr-4": {
- "Zend\\Mvc\\": "src/"
+ "Zend\\Mvc\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
- "homepage": "https://github.com/zendframework/zend-mvc",
+ "homepage": "https://github.com/zendframework/zf2",
"keywords": [
"mvc",
"zf2"
],
- "time": "2015-03-26 18:55:14"
+ "time": "2015-04-01 18:09:27"
},
{
"name": "zendframework/zend-serializer",
@@ -1957,12 +1894,12 @@
"source": {
"type": "git",
"url": "https://github.com/zendframework/zend-serializer.git",
- "reference": "3c531789a9882a5deb721356a7bd2642b65d4b09"
+ "reference": "a46960854d6326f0036d98c9abc7a79e36e25928"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/zendframework/zend-serializer/zipball/3c531789a9882a5deb721356a7bd2642b65d4b09",
- "reference": "3c531789a9882a5deb721356a7bd2642b65d4b09",
+ "reference": "a46960854d6326f0036d98c9abc7a79e36e25928",
"shasum": ""
},
"require": {
@@ -1972,9 +1909,6 @@
"zendframework/zend-stdlib": "self.version"
},
"require-dev": {
- "fabpot/php-cs-fixer": "1.7.*",
- "phpunit/phpunit": "~4.0",
- "satooshi/php-coveralls": "dev-master",
"zendframework/zend-servicemanager": "self.version"
},
"suggest": {
@@ -1989,7 +1923,7 @@
},
"autoload": {
"psr-4": {
- "Zend\\Serializer\\": "src/"
+ "Zend\\Serializer\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -1997,12 +1931,12 @@
"BSD-3-Clause"
],
"description": "provides an adapter based interface to simply generate storable representation of PHP types by different facilities, and recover",
- "homepage": "https://github.com/zendframework/zend-serializer",
+ "homepage": "https://github.com/zendframework/zf2",
"keywords": [
"serializer",
"zf2"
],
- "time": "2015-03-25 20:55:48"
+ "time": "2015-04-01 18:09:28"
},
{
"name": "zendframework/zend-server",
@@ -2010,12 +1944,12 @@
"source": {
"type": "git",
"url": "https://github.com/zendframework/zend-server.git",
- "reference": "d11ff0bd529d202022823d4accf5983cbd50fc49"
+ "reference": "fc73c34490908ba143af3c57c7e166b40c4b9f8e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/zendframework/zend-server/zipball/d11ff0bd529d202022823d4accf5983cbd50fc49",
- "reference": "d11ff0bd529d202022823d4accf5983cbd50fc49",
+ "reference": "fc73c34490908ba143af3c57c7e166b40c4b9f8e",
"shasum": ""
},
"require": {
@@ -2023,11 +1957,6 @@
"zendframework/zend-code": "self.version",
"zendframework/zend-stdlib": "self.version"
},
- "require-dev": {
- "fabpot/php-cs-fixer": "1.7.*",
- "phpunit/phpunit": "~4.0",
- "satooshi/php-coveralls": "dev-master"
- },
"type": "library",
"extra": {
"branch-alias": {
@@ -2037,19 +1966,19 @@
},
"autoload": {
"psr-4": {
- "Zend\\Server\\": "src/"
+ "Zend\\Server\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
- "homepage": "https://github.com/zendframework/zend-server",
+ "homepage": "https://github.com/zendframework/zf2",
"keywords": [
"server",
"zf2"
],
- "time": "2015-03-25 20:55:48"
+ "time": "2015-04-01 18:09:28"
},
{
"name": "zendframework/zend-servicemanager",
@@ -2057,21 +1986,18 @@
"source": {
"type": "git",
"url": "https://github.com/zendframework/zend-servicemanager.git",
- "reference": "57cf99fa5ac08c05a135a8d0d676c52a5e450083"
+ "reference": "d3c27c708a148a30608f313a5b7a61a531bd9cb9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/zendframework/zend-servicemanager/zipball/57cf99fa5ac08c05a135a8d0d676c52a5e450083",
- "reference": "57cf99fa5ac08c05a135a8d0d676c52a5e450083",
+ "reference": "d3c27c708a148a30608f313a5b7a61a531bd9cb9",
"shasum": ""
},
"require": {
"php": ">=5.3.23"
},
"require-dev": {
- "fabpot/php-cs-fixer": "1.7.*",
- "phpunit/phpunit": "~4.0",
- "satooshi/php-coveralls": "dev-master",
"zendframework/zend-di": "self.version"
},
"suggest": {
@@ -2087,19 +2013,19 @@
},
"autoload": {
"psr-4": {
- "Zend\\ServiceManager\\": "src/"
+ "Zend\\ServiceManager\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
- "homepage": "https://github.com/zendframework/zend-service-manager",
+ "homepage": "https://github.com/zendframework/zf2",
"keywords": [
"servicemanager",
"zf2"
],
- "time": "2015-03-23 18:29:14"
+ "time": "2015-04-01 18:09:28"
},
{
"name": "zendframework/zend-soap",
@@ -2107,12 +2033,12 @@
"source": {
"type": "git",
"url": "https://github.com/zendframework/zend-soap.git",
- "reference": "a599463aba97ce247faf3fb443e3c7858b46449b"
+ "reference": "e42b900798ea95a9063fa4922da976d8b3a8ab6f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/zendframework/zend-soap/zipball/a599463aba97ce247faf3fb443e3c7858b46449b",
- "reference": "a599463aba97ce247faf3fb443e3c7858b46449b",
+ "reference": "e42b900798ea95a9063fa4922da976d8b3a8ab6f",
"shasum": ""
},
"require": {
@@ -2122,9 +2048,6 @@
"zendframework/zend-uri": "self.version"
},
"require-dev": {
- "fabpot/php-cs-fixer": "1.7.*",
- "phpunit/phpunit": "~4.0",
- "satooshi/php-coveralls": "dev-master",
"zendframework/zend-http": "self.version"
},
"suggest": {
@@ -2139,19 +2062,19 @@
},
"autoload": {
"psr-4": {
- "Zend\\Soap\\": "src/"
+ "Zend\\Soap\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
- "homepage": "https://github.com/zendframework/zend-soap",
+ "homepage": "https://github.com/zendframework/zf2",
"keywords": [
"soap",
"zf2"
],
- "time": "2015-03-25 20:55:48"
+ "time": "2015-04-01 18:09:29"
},
{
"name": "zendframework/zend-stdlib",
@@ -2159,21 +2082,18 @@
"source": {
"type": "git",
"url": "https://github.com/zendframework/zend-stdlib.git",
- "reference": "cf05c5ba75606e47ffee91cedc72778da46f74c3"
+ "reference": "eab586f4c18af3fa63c977611939f1f4a3cf1030"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/zendframework/zend-stdlib/zipball/cf05c5ba75606e47ffee91cedc72778da46f74c3",
- "reference": "cf05c5ba75606e47ffee91cedc72778da46f74c3",
+ "reference": "eab586f4c18af3fa63c977611939f1f4a3cf1030",
"shasum": ""
},
"require": {
"php": ">=5.3.23"
},
"require-dev": {
- "fabpot/php-cs-fixer": "1.7.*",
- "phpunit/phpunit": "~4.0",
- "satooshi/php-coveralls": "dev-master",
"zendframework/zend-eventmanager": "self.version",
"zendframework/zend-filter": "self.version",
"zendframework/zend-serializer": "self.version",
@@ -2194,19 +2114,19 @@
},
"autoload": {
"psr-4": {
- "Zend\\Stdlib\\": "src/"
+ "Zend\\Stdlib\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
- "homepage": "https://github.com/zendframework/zend-stdlib",
+ "homepage": "https://github.com/zendframework/zf2",
"keywords": [
"stdlib",
"zf2"
],
- "time": "2015-03-25 20:55:48"
+ "time": "2015-04-01 18:09:29"
},
{
"name": "zendframework/zend-text",
@@ -2214,12 +2134,12 @@
"source": {
"type": "git",
"url": "https://github.com/zendframework/zend-text.git",
- "reference": "d962ea25647b20527f3ca34ae225bbc885dabfc7"
+ "reference": "35f519e20e575a331c2ee554e5a555a59ce4b9e2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/zendframework/zend-text/zipball/d962ea25647b20527f3ca34ae225bbc885dabfc7",
- "reference": "d962ea25647b20527f3ca34ae225bbc885dabfc7",
+ "reference": "35f519e20e575a331c2ee554e5a555a59ce4b9e2",
"shasum": ""
},
"require": {
@@ -2227,11 +2147,6 @@
"zendframework/zend-servicemanager": "self.version",
"zendframework/zend-stdlib": "self.version"
},
- "require-dev": {
- "fabpot/php-cs-fixer": "1.7.*",
- "phpunit/phpunit": "~4.0",
- "satooshi/php-coveralls": "dev-master"
- },
"type": "library",
"extra": {
"branch-alias": {
@@ -2241,19 +2156,19 @@
},
"autoload": {
"psr-4": {
- "Zend\\Text\\": "src/"
+ "Zend\\Text\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
- "homepage": "https://github.com/zendframework/zend-text",
+ "homepage": "https://github.com/zendframework/zf2",
"keywords": [
"text",
"zf2"
],
- "time": "2015-03-25 20:55:48"
+ "time": "2015-04-01 18:09:29"
},
{
"name": "zendframework/zend-uri",
@@ -2261,12 +2176,12 @@
"source": {
"type": "git",
"url": "https://github.com/zendframework/zend-uri.git",
- "reference": "bd9e625639415376f6a82551c73328448d7bc7d1"
+ "reference": "53f5b162b293f80de8b951eece8e08be83c4fe16"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/zendframework/zend-uri/zipball/bd9e625639415376f6a82551c73328448d7bc7d1",
- "reference": "bd9e625639415376f6a82551c73328448d7bc7d1",
+ "reference": "53f5b162b293f80de8b951eece8e08be83c4fe16",
"shasum": ""
},
"require": {
@@ -2274,11 +2189,6 @@
"zendframework/zend-escaper": "self.version",
"zendframework/zend-validator": "self.version"
},
- "require-dev": {
- "fabpot/php-cs-fixer": "1.7.*",
- "phpunit/phpunit": "~4.0",
- "satooshi/php-coveralls": "dev-master"
- },
"type": "library",
"extra": {
"branch-alias": {
@@ -2288,7 +2198,7 @@
},
"autoload": {
"psr-4": {
- "Zend\\Uri\\": "src/"
+ "Zend\\Uri\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -2296,12 +2206,12 @@
"BSD-3-Clause"
],
"description": "a component that aids in manipulating and validating » Uniform Resource Identifiers (URIs)",
- "homepage": "https://github.com/zendframework/zend-uri",
+ "homepage": "https://github.com/zendframework/zf2",
"keywords": [
"uri",
"zf2"
],
- "time": "2015-03-25 20:55:48"
+ "time": "2015-04-01 18:09:29"
},
{
"name": "zendframework/zend-validator",
@@ -2309,12 +2219,12 @@
"source": {
"type": "git",
"url": "https://github.com/zendframework/zend-validator.git",
- "reference": "45fac2545a0f2eb66d71cb7966feee481e7c475f"
+ "reference": "eb678d20256f120a72ca27276bbb2875841701ab"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/zendframework/zend-validator/zipball/45fac2545a0f2eb66d71cb7966feee481e7c475f",
- "reference": "45fac2545a0f2eb66d71cb7966feee481e7c475f",
+ "reference": "eb678d20256f120a72ca27276bbb2875841701ab",
"shasum": ""
},
"require": {
@@ -2322,9 +2232,6 @@
"zendframework/zend-stdlib": "self.version"
},
"require-dev": {
- "fabpot/php-cs-fixer": "1.7.*",
- "phpunit/phpunit": "~4.0",
- "satooshi/php-coveralls": "dev-master",
"zendframework/zend-db": "self.version",
"zendframework/zend-filter": "self.version",
"zendframework/zend-i18n": "self.version",
@@ -2352,7 +2259,7 @@
},
"autoload": {
"psr-4": {
- "Zend\\Validator\\": "src/"
+ "Zend\\Validator\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -2360,12 +2267,12 @@
"BSD-3-Clause"
],
"description": "provides a set of commonly needed validators",
- "homepage": "https://github.com/zendframework/zend-validator",
+ "homepage": "https://github.com/zendframework/zf2",
"keywords": [
"validator",
"zf2"
],
- "time": "2015-03-25 20:55:48"
+ "time": "2015-04-01 18:09:30"
},
{
"name": "zendframework/zend-view",
@@ -2373,12 +2280,12 @@
"source": {
"type": "git",
"url": "https://github.com/zendframework/zend-view.git",
- "reference": "37beb1ad46e530f627b4b6c3716efd728e976ba9"
+ "reference": "e119b4b5f082af58a96eb206e782b62c193227bf"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/zendframework/zend-view/zipball/37beb1ad46e530f627b4b6c3716efd728e976ba9",
- "reference": "37beb1ad46e530f627b4b6c3716efd728e976ba9",
+ "reference": "e119b4b5f082af58a96eb206e782b62c193227bf",
"shasum": ""
},
"require": {
@@ -2388,9 +2295,6 @@
"zendframework/zend-stdlib": "self.version"
},
"require-dev": {
- "fabpot/php-cs-fixer": "1.7.*",
- "phpunit/phpunit": "~4.0",
- "satooshi/php-coveralls": "dev-master",
"zendframework/zend-authentication": "self.version",
"zendframework/zend-escaper": "self.version",
"zendframework/zend-feed": "self.version",
@@ -2429,7 +2333,7 @@
},
"autoload": {
"psr-4": {
- "Zend\\View\\": "src/"
+ "Zend\\View\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -2437,12 +2341,12 @@
"BSD-3-Clause"
],
"description": "provides a system of helpers, output filters, and variable escaping",
- "homepage": "https://github.com/zendframework/zend-view",
+ "homepage": "https://github.com/zendframework/zf2",
"keywords": [
"view",
"zf2"
],
- "time": "2015-03-25 20:55:48"
+ "time": "2015-04-01 18:09:30"
}
],
"packages-dev": [
@@ -3584,16 +3488,16 @@
},
{
"name": "symfony/config",
- "version": "v2.7.3",
+ "version": "v2.7.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/Config.git",
- "reference": "6c905bbed1e728226de656e4c07d620dfe9e80d9"
+ "reference": "5ab9ff48b3cb5b40951a607f77fc1cbfd29edba8"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/Config/zipball/6c905bbed1e728226de656e4c07d620dfe9e80d9",
- "reference": "6c905bbed1e728226de656e4c07d620dfe9e80d9",
+ "url": "https://api.github.com/repos/symfony/Config/zipball/5ab9ff48b3cb5b40951a607f77fc1cbfd29edba8",
+ "reference": "5ab9ff48b3cb5b40951a607f77fc1cbfd29edba8",
"shasum": ""
},
"require": {
@@ -3630,20 +3534,20 @@
],
"description": "Symfony Config Component",
"homepage": "https://symfony.com",
- "time": "2015-07-09 16:07:40"
+ "time": "2015-08-27 06:45:45"
},
{
"name": "symfony/dependency-injection",
- "version": "v2.7.3",
+ "version": "v2.7.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/DependencyInjection.git",
- "reference": "851e3ffe8a366b1590bdaf3df2c1395f2d27d8a6"
+ "reference": "c0a3a97b9450d77cd8eff81c5825efb3624c255b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/DependencyInjection/zipball/851e3ffe8a366b1590bdaf3df2c1395f2d27d8a6",
- "reference": "851e3ffe8a366b1590bdaf3df2c1395f2d27d8a6",
+ "url": "https://api.github.com/repos/symfony/DependencyInjection/zipball/c0a3a97b9450d77cd8eff81c5825efb3624c255b",
+ "reference": "c0a3a97b9450d77cd8eff81c5825efb3624c255b",
"shasum": ""
},
"require": {
@@ -3690,20 +3594,20 @@
],
"description": "Symfony DependencyInjection Component",
"homepage": "https://symfony.com",
- "time": "2015-07-28 14:07:07"
+ "time": "2015-08-24 07:16:32"
},
{
"name": "symfony/event-dispatcher",
- "version": "v2.7.3",
+ "version": "v2.7.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/EventDispatcher.git",
- "reference": "9310b5f9a87ec2ea75d20fec0b0017c77c66dac3"
+ "reference": "b58c916f1db03a611b72dd702564f30ad8fe83fa"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/9310b5f9a87ec2ea75d20fec0b0017c77c66dac3",
- "reference": "9310b5f9a87ec2ea75d20fec0b0017c77c66dac3",
+ "url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/b58c916f1db03a611b72dd702564f30ad8fe83fa",
+ "reference": "b58c916f1db03a611b72dd702564f30ad8fe83fa",
"shasum": ""
},
"require": {
@@ -3748,20 +3652,20 @@
],
"description": "Symfony EventDispatcher Component",
"homepage": "https://symfony.com",
- "time": "2015-06-18 19:21:56"
+ "time": "2015-08-24 07:13:45"
},
{
"name": "symfony/filesystem",
- "version": "v2.7.3",
+ "version": "v2.7.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/Filesystem.git",
- "reference": "2d7b2ddaf3f548f4292df49a99d19c853d43f0b8"
+ "reference": "f079e9933799929584200b9a926f72f29e291654"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/Filesystem/zipball/2d7b2ddaf3f548f4292df49a99d19c853d43f0b8",
- "reference": "2d7b2ddaf3f548f4292df49a99d19c853d43f0b8",
+ "url": "https://api.github.com/repos/symfony/Filesystem/zipball/f079e9933799929584200b9a926f72f29e291654",
+ "reference": "f079e9933799929584200b9a926f72f29e291654",
"shasum": ""
},
"require": {
@@ -3797,20 +3701,20 @@
],
"description": "Symfony Filesystem Component",
"homepage": "https://symfony.com",
- "time": "2015-07-09 16:07:40"
+ "time": "2015-08-27 07:03:44"
},
{
"name": "symfony/stopwatch",
- "version": "v2.7.3",
+ "version": "v2.7.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/Stopwatch.git",
- "reference": "b07a866719bbac5294c67773340f97b871733310"
+ "reference": "abc61bac76fb10ffa2c6373d7932bc35190dbf3b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/Stopwatch/zipball/b07a866719bbac5294c67773340f97b871733310",
- "reference": "b07a866719bbac5294c67773340f97b871733310",
+ "url": "https://api.github.com/repos/symfony/Stopwatch/zipball/abc61bac76fb10ffa2c6373d7932bc35190dbf3b",
+ "reference": "abc61bac76fb10ffa2c6373d7932bc35190dbf3b",
"shasum": ""
},
"require": {
@@ -3846,20 +3750,20 @@
],
"description": "Symfony Stopwatch Component",
"homepage": "https://symfony.com",
- "time": "2015-07-01 18:23:16"
+ "time": "2015-08-24 07:13:45"
},
{
"name": "symfony/yaml",
- "version": "v2.7.3",
+ "version": "v2.7.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/Yaml.git",
- "reference": "71340e996171474a53f3d29111d046be4ad8a0ff"
+ "reference": "2dc7b06c065df96cc686c66da2705e5e18aef661"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/Yaml/zipball/71340e996171474a53f3d29111d046be4ad8a0ff",
- "reference": "71340e996171474a53f3d29111d046be4ad8a0ff",
+ "url": "https://api.github.com/repos/symfony/Yaml/zipball/2dc7b06c065df96cc686c66da2705e5e18aef661",
+ "reference": "2dc7b06c065df96cc686c66da2705e5e18aef661",
"shasum": ""
},
"require": {
@@ -3895,7 +3799,7 @@
],
"description": "Symfony Yaml Component",
"homepage": "https://symfony.com",
- "time": "2015-07-28 14:07:07"
+ "time": "2015-08-24 07:13:45"
}
],
"aliases": [],
diff --git a/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/AddressTest.php b/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/AddressTest.php
index df6a03e4fbc70..a4dd5f80b41fb 100644
--- a/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/AddressTest.php
+++ b/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/AddressTest.php
@@ -110,11 +110,11 @@ public function testConstruct()
// check message templates
$this->assertAttributeInternalType(
'array',
- '_messageTemplates',
+ 'errorMessageTemplates',
$this->_entityAdapter,
'Templates must be an array.'
);
- $this->assertAttributeNotEmpty('_messageTemplates', $this->_entityAdapter, 'Templates must not be empty');
+ $this->assertAttributeNotEmpty('errorMessageTemplates', $this->_entityAdapter, 'Templates must not be empty');
// check attributes
$this->assertAttributeInternalType(
@@ -398,7 +398,9 @@ public function testImportDataAddUpdate()
$result = $this->_entityAdapter->setSource(
\Magento\ImportExport\Model\Import\Adapter::findAdapterFor($sourceFile, $directoryWrite)
- )->isDataValid();
+ )
+ ->validateData()
+ ->hasToBeTerminated();
$this->assertFalse($result, 'Validation result must be false.');
// import data
@@ -492,8 +494,8 @@ public function testImportDataDelete()
$directoryWrite = $filesystem->getDirectoryWrite(DirectoryList::ROOT);
$result = $this->_entityAdapter->setSource(
\Magento\ImportExport\Model\Import\Adapter::findAdapterFor($sourceFile, $directoryWrite)
- )->isDataValid();
- $this->assertTrue($result, 'Validation result must be true.');
+ )->validateData()->hasToBeTerminated();
+ $this->assertTrue(!$result, 'Validation result must be true.');
// import data
$this->_entityAdapter->importData();
diff --git a/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/CustomerCompositeTest.php b/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/CustomerCompositeTest.php
index e241fdc28639d..1ac06a6a9110a 100644
--- a/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/CustomerCompositeTest.php
+++ b/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/CustomerCompositeTest.php
@@ -6,6 +6,7 @@
namespace Magento\CustomerImportExport\Model\Import;
use Magento\Framework\App\Filesystem\DirectoryList;
+use Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingErrorAggregatorInterface;
class CustomerCompositeTest extends \PHPUnit_Framework_TestCase
{
@@ -145,19 +146,26 @@ public function testImportData($behavior, $sourceFile, array $dataBefore, array
$filesystem = $this->_objectManager->create('Magento\Framework\Filesystem');
$rootDirectory = $filesystem->getDirectoryWrite(DirectoryList::ROOT);
+ $this->_entityAdapter->getErrorAggregator()->initValidationStrategy(
+ ProcessingErrorAggregatorInterface::VALIDATION_STRATEGY_STOP_ON_ERROR,
+ 10
+ );
+
// set fixture CSV file
$result = $this->_entityAdapter->setSource(
\Magento\ImportExport\Model\Import\Adapter::findAdapterFor($sourceFile, $rootDirectory)
- )->isDataValid();
+ )
+ ->validateData()
+ ->hasToBeTerminated();
if ($errors) {
- $this->assertFalse($result);
- } else {
$this->assertTrue($result);
+ } else {
+ $this->assertFalse($result);
}
// assert validation errors
// can't use error codes because entity adapter gathers only error messages from aggregated adapters
- $actualErrors = array_values($this->_entityAdapter->getErrorMessages());
+ $actualErrors = array_values($this->_entityAdapter->getErrorAggregator()->getRowsGroupedByErrorCode());
$this->assertEquals($errors, $actualErrors);
// assert data before import
@@ -192,7 +200,7 @@ public function importDataDataProvider()
'$sourceFile' => $filesDirectory . self::UPDATE_FILE_NAME,
'$dataBefore' => $this->_beforeImport,
'$dataAfter' => $this->_afterImport,
- '$errors' => [[6]], // row #6 has no website
+ '$errors' => [],
];
return $sourceData;
diff --git a/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/CustomerTest.php b/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/CustomerTest.php
index 802d203628aaa..e9ff2b4c0045e 100644
--- a/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/CustomerTest.php
+++ b/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/CustomerTest.php
@@ -46,7 +46,7 @@ protected function setUp()
->create('Magento\CustomerImportExport\Model\Import\Customer');
$this->_model->setParameters(['behavior' => Import::BEHAVIOR_ADD_UPDATE]);
- $propertyAccessor = new \ReflectionProperty($this->_model, '_messageTemplates');
+ $propertyAccessor = new \ReflectionProperty($this->_model, 'errorMessageTemplates');
$propertyAccessor->setAccessible(true);
$propertyAccessor->setValue($this->_model, []);
@@ -98,7 +98,8 @@ public function testImportData()
$this->_model
->setParameters(['behavior' => Import::BEHAVIOR_ADD_UPDATE])
->setSource($source)
- ->isDataValid();
+ ->validateData()
+ ->hasToBeTerminated();
$this->_model->importData();
@@ -148,7 +149,7 @@ public function testDeleteData()
\Magento\TestFramework\Helper\Bootstrap::getInstance()
->loadArea(\Magento\Framework\App\Area::AREA_FRONTEND);
$source = new \Magento\ImportExport\Model\Import\Source\Csv(
- __DIR__ . '/_files/customers_to_import.csv',
+ __DIR__ . '/_files/customers_to_delete.csv',
$this->directoryWrite
);
@@ -158,11 +159,9 @@ public function testDeleteData()
);
$this->assertEquals(3, $customerCollection->count(), 'Count of existing customers are invalid');
- $this->_model->setParameters(
- ['behavior' => Import::BEHAVIOR_DELETE]
- )->setSource(
- $source
- )->isDataValid();
+ $this->_model->setParameters(['behavior' => Import::BEHAVIOR_DELETE])
+ ->setSource($source)
+ ->validateData();
$this->_model->importData();
@@ -178,68 +177,75 @@ public function testGetEntityTypeCode()
public function testValidateRowDuplicateEmail()
{
+ $this->_model->getErrorAggregator()->clear();
$this->_model->validateRow($this->_customerData, 0);
- $this->assertEquals(0, $this->_model->getErrorsCount());
+ $this->assertEquals(0, $this->_model->getErrorAggregator()->getErrorsCount());
$this->_customerData[Customer::COLUMN_EMAIL] = strtoupper(
$this->_customerData[Customer::COLUMN_EMAIL]
);
$this->_model->validateRow($this->_customerData, 1);
- $this->assertEquals(1, $this->_model->getErrorsCount());
- $this->assertArrayHasKey(
- Customer::ERROR_DUPLICATE_EMAIL_SITE,
- $this->_model->getErrorMessages()
+ $this->assertEquals(1, $this->_model->getErrorAggregator()->getErrorsCount());
+ $this->assertNotEmpty(
+ $this->_model->getErrorAggregator()->getErrorsByCode([Customer::ERROR_DUPLICATE_EMAIL_SITE]
+ )
);
}
public function testValidateRowInvalidEmail()
{
+ $this->_model->getErrorAggregator()->clear();
$this->_customerData[Customer::COLUMN_EMAIL] = 'wrong_email@format';
$this->_model->validateRow($this->_customerData, 0);
- $this->assertEquals(1, $this->_model->getErrorsCount());
- $this->assertArrayHasKey(
- Customer::ERROR_INVALID_EMAIL,
- $this->_model->getErrorMessages()
+ $this->assertEquals(1, $this->_model->getErrorAggregator()->getErrorsCount());
+ $this->assertNotEmpty(
+ $this->_model->getErrorAggregator()->getErrorsByCode([Customer::ERROR_INVALID_EMAIL]
+ )
);
}
public function testValidateRowInvalidWebsite()
{
+ $this->_model->getErrorAggregator()->clear();
$this->_customerData[Customer::COLUMN_WEBSITE] = 'not_existing_web_site';
$this->_model->validateRow($this->_customerData, 0);
- $this->assertEquals(1, $this->_model->getErrorsCount());
- $this->assertArrayHasKey(
- Customer::ERROR_INVALID_WEBSITE,
- $this->_model->getErrorMessages()
+ $this->assertEquals(1, $this->_model->getErrorAggregator()->getErrorsCount());
+ $this->assertNotEmpty(
+ $this->_model->getErrorAggregator()->getErrorsByCode([Customer::ERROR_INVALID_WEBSITE]
+ )
);
}
public function testValidateRowInvalidStore()
{
+ $this->_model->getErrorAggregator()->clear();
$this->_customerData[Customer::COLUMN_STORE] = 'not_existing_web_store';
$this->_model->validateRow($this->_customerData, 0);
- $this->assertEquals(1, $this->_model->getErrorsCount());
- $this->assertArrayHasKey(
- Customer::ERROR_INVALID_STORE,
- $this->_model->getErrorMessages()
+ $this->assertEquals(1, $this->_model->getErrorAggregator()->getErrorsCount());
+ $this->assertNotEmpty(
+ $this->_model->getErrorAggregator()->getErrorsByCode([Customer::ERROR_INVALID_STORE]
+ )
);
}
public function testValidateRowPasswordLengthIncorrect()
{
+ $this->_model->getErrorAggregator()->clear();
$this->_customerData['password'] = '12345';
$this->_model->validateRow($this->_customerData, 0);
- $this->assertEquals(1, $this->_model->getErrorsCount());
- $this->assertArrayHasKey(
- Customer::ERROR_PASSWORD_LENGTH, $this->_model->getErrorMessages()
+ $this->assertEquals(1, $this->_model->getErrorAggregator()->getErrorsCount());
+ $this->assertNotEmpty(
+ $this->_model->getErrorAggregator()->getErrorsByCode([Customer::ERROR_PASSWORD_LENGTH]
+ )
);
}
public function testValidateRowPasswordLengthCorrect()
{
+ $this->_model->getErrorAggregator()->clear();
$this->_customerData['password'] = '1234567890';
$this->_model->validateRow($this->_customerData, 0);
- $this->assertEquals(0, $this->_model->getErrorsCount());
+ $this->assertEquals(0, $this->_model->getErrorAggregator()->getErrorsCount());
}
/**
@@ -247,27 +253,33 @@ public function testValidateRowPasswordLengthCorrect()
*/
public function testValidateRowAttributeRequired()
{
+ $this->_model->getErrorAggregator()->clear();
unset($this->_customerData['firstname']);
unset($this->_customerData['lastname']);
unset($this->_customerData['group_id']);
$this->_model->validateRow($this->_customerData, 0);
- $this->assertEquals(0, $this->_model->getErrorsCount());
+ $this->assertEquals(0, $this->_model->getErrorAggregator()->getErrorsCount());
$this->_customerData[Customer::COLUMN_EMAIL] = 'new.customer@example.com';
$this->_model->validateRow($this->_customerData, 1);
- $this->assertGreaterThan(0, $this->_model->getErrorsCount());
- $this->assertArrayHasKey(Customer::ERROR_VALUE_IS_REQUIRED, $this->_model->getErrorMessages());
+ $this->assertGreaterThan(0, $this->_model->getErrorAggregator()->getErrorsCount());
+ $this->assertNotEmpty(
+ $this->_model->getErrorAggregator()->getErrorsByCode([Customer::ERROR_VALUE_IS_REQUIRED]
+ )
+ );
}
public function testValidateEmailForDeleteBehavior()
{
+ $this->_model->getErrorAggregator()->clear();
$this->_customerData[Customer::COLUMN_EMAIL] = 'new.customer@example.com';
$this->_model->setParameters(['behavior' => Import::BEHAVIOR_DELETE]);
$this->_model->validateRow($this->_customerData, 0);
- $this->assertGreaterThan(0, $this->_model->getErrorsCount());
- $this->assertArrayHasKey(
- Customer::ERROR_CUSTOMER_NOT_FOUND, $this->_model->getErrorMessages()
+ $this->assertGreaterThan(0, $this->_model->getErrorAggregator()->getErrorsCount());
+ $this->assertNotEmpty(
+ $this->_model->getErrorAggregator()->getErrorsByCode([Customer::ERROR_CUSTOMER_NOT_FOUND]
+ )
);
}
}
diff --git a/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/_files/address_import_update.csv b/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/_files/address_import_update.csv
index 76fbbc72911e6..936184ce967e8 100644
--- a/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/_files/address_import_update.csv
+++ b/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/_files/address_import_update.csv
@@ -1,5 +1,4 @@
"_website","_email","_entity_id","city","company","country_id","fax","firstname","lastname","middlename","postcode","prefix","region","region_id","street","suffix","telephone","vat_id","vat_is_valid","vat_request_date","vat_request_id","vat_request_success","_address_default_billing_","_address_default_shipping_"
"admin","BetsyParker@example.com",1,,,"US",,"Katy","Parker","T.",19107,,,,"1079 Rocky Road",,"215-629-9720",,,,,,,1
"admin","BetsyParker@example.com",3,"Phoenix",,"US",,"Brad","Brown","H.",85034,,"Arizona",4,"4225 Martha Street",,"928-707-1577",,,,,,1,
-"admin","JenniferCJackson@teleworm.us",4,"Tampa",,"US",,"Jennifer ","Jackson","C.",33602,,"Florida",18,"1192 Maryland Avenue",,"727-555-0854",,,,,,,
"admin","BetsyParker@example.com",,"Tallahassee",,"US",,"William ","Compton","M.",32301,,"Florida",18,"1973 Drainer Avenue",,"850-590-7403",,,,,,,
diff --git a/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/_files/customer_composite_update.csv b/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/_files/customer_composite_update.csv
index 96fa62191dff9..dfe6ee546efd0 100644
--- a/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/_files/customer_composite_update.csv
+++ b/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/_files/customer_composite_update.csv
@@ -4,4 +4,3 @@
"LoriBBanks@magento.com","admin","admin",,"05/06/2012 15:59","Admin",0,,"Lori","Female",1,"Banks","B.","7ad6dbdc83d3e9f598825dc58b84678c7351e4281f6bc2b277a32dcd88b9756b:pz",,,,0,,,0,,"Wenatchee",,"US",,"Lori","Banks",,98801,,"Washington","2573 Goodwin Avenue",,"509-421-4364",,1,1
"BetsyParker@example.com","admin","admin",,"05/06/2012 16:13","Admin",0,,"NotBetsy","Female",1,"NotParker","H.","145d12bfff8a6a279eb61e277e3d727c0ba95acc1131237f1594ddbb7687a564:l1",,,,0,,,2,,"Philadelphia",,"US",,"Betsy","Parker",,19108,,"Pennsylvania","1079 Rocky Road 1079 Rocky Road 2",,"215-629-9720",,1,1
"KellyNIlson@magento.com","base","admin",,"05/06/2012 17:11","Admin",0,,"Kelly","Female",1,"Nilson","H.","145d12bfff8a6a279eb61e277e3d727c0ba95acc1131237f1594ddbb7687a564:l1",,,,0,,,2,,,,,,,,,,,,,,,,,
-"MichaelJackson@magento.com",,"admin",,"05/06/2012 17:11","Admin",0,,"Michael","Male",1,"Jackson","J.","145d12bfff8a6a279eb61e277e3d727c0ba95acc1131237f1594ddbb7687a564:l1",,,,0,,,2,,,,,,,,,,,,,,,,,
diff --git a/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/_files/customers_to_delete.csv b/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/_files/customers_to_delete.csv
new file mode 100644
index 0000000000000..0c7ebed0ec47d
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/_files/customers_to_delete.csv
@@ -0,0 +1,4 @@
+email,_website,_store,confirmation,created_at,created_in,default_billing,default_shipping,disable_auto_group_change,dob,firstname,gender,group_id,lastname,middlename,password_hash,prefix,rp_token,rp_token_created_at,store_id,suffix,taxvat,website_id,password
+customer@example.com,base,admin,,5/6/2012 16:15,Admin,4,4,0,,Firstname,Male,1,Lastname,T.,145d12bfff8a6a279eb61e277e3d727c0ba95acc1131237f1594ddbb7687a564:l1,,,,0,,,2,
+julie.worrell@example.com,base,admin,,5/6/2012 16:19,Admin,4,4,0,,Julie,Female,1,Worrell,T.,145d12bfff8a6a279eb61e277e3d727c0ba95acc1131237f1594ddbb7687a564:l1,,,,0,,,2,
+david.lamar@example.com,base,admin,,5/6/2012 16:25,Admin,4,4,0,,David,Male,1,Lamar,T.,145d12bfff8a6a279eb61e277e3d727c0ba95acc1131237f1594ddbb7687a564:l1,,,,0,,,2,
diff --git a/dev/tests/integration/testsuite/Magento/ImportExport/Model/Import/EntityAbstractTest.php b/dev/tests/integration/testsuite/Magento/ImportExport/Model/Import/EntityAbstractTest.php
index 6ef46093056d6..7098903cb7e8e 100644
--- a/dev/tests/integration/testsuite/Magento/ImportExport/Model/Import/EntityAbstractTest.php
+++ b/dev/tests/integration/testsuite/Magento/ImportExport/Model/Import/EntityAbstractTest.php
@@ -37,7 +37,10 @@ public function testSaveValidatedBunches()
$objectManager->get('Magento\Framework\App\Config\ScopeConfigInterface'),
$objectManager->get('Magento\ImportExport\Model\ImportFactory'),
$objectManager->get('Magento\ImportExport\Model\Resource\Helper'),
- $objectManager->get('Magento\Framework\App\Resource')
+ $objectManager->get('Magento\Framework\App\Resource'),
+ $objectManager->get(
+ 'Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingErrorAggregatorInterface'
+ )
],
'',
true,
diff --git a/dev/tests/integration/testsuite/Magento/ImportExport/Model/ImportTest.php b/dev/tests/integration/testsuite/Magento/ImportExport/Model/ImportTest.php
index 788e55647ad7b..cdc7979569f1b 100644
--- a/dev/tests/integration/testsuite/Magento/ImportExport/Model/ImportTest.php
+++ b/dev/tests/integration/testsuite/Magento/ImportExport/Model/ImportTest.php
@@ -5,6 +5,8 @@
*/
namespace Magento\ImportExport\Model;
+use Magento\ImportExport\Model\Import;
+
/**
* @magentoDataFixture Magento/ImportExport/_files/import_data.php
*/
@@ -13,7 +15,7 @@ class ImportTest extends \PHPUnit_Framework_TestCase
/**
* Model object which is used for tests
*
- * @var \Magento\ImportExport\Model\Import
+ * @var Import
*/
protected $_model;
@@ -31,18 +33,24 @@ class ImportTest extends \PHPUnit_Framework_TestCase
'catalog_product' => [
'token' => 'Magento\ImportExport\Model\Source\Import\Behavior\Basic',
'code' => 'basic_behavior',
+ 'notes' => [
+ \Magento\ImportExport\Model\Import::BEHAVIOR_REPLACE => "Note: Product IDs will be regenerated."
+ ],
],
'customer_composite' => [
'token' => 'Magento\ImportExport\Model\Source\Import\Behavior\Basic',
'code' => 'basic_behavior',
+ 'notes' => [],
],
'customer' => [
'token' => 'Magento\ImportExport\Model\Source\Import\Behavior\Custom',
'code' => 'custom_behavior',
+ 'notes' => [],
],
'customer_address' => [
'token' => 'Magento\ImportExport\Model\Source\Import\Behavior\Custom',
'code' => 'custom_behavior',
+ 'notes' => [],
],
];
@@ -82,6 +90,10 @@ public function testImportSource()
$customersCollection->resetData();
$customersCollection->clear();
+ $this->_model->setData(
+ Import::FIELD_NAME_VALIDATION_STRATEGY,
+ Import\ErrorProcessing\ProcessingErrorAggregatorInterface::VALIDATION_STRATEGY_SKIP_ERRORS
+ );
$this->_model->importSource();
$customers = $customersCollection->getItems();
diff --git a/setup/src/Magento/Setup/Fixtures/ConfigurableProductsFixture.php b/setup/src/Magento/Setup/Fixtures/ConfigurableProductsFixture.php
index 0112fd010caef..a5b6ad9b38dcf 100644
--- a/setup/src/Magento/Setup/Fixtures/ConfigurableProductsFixture.php
+++ b/setup/src/Magento/Setup/Fixtures/ConfigurableProductsFixture.php
@@ -654,7 +654,13 @@ public function execute()
/** @var \Magento\ImportExport\Model\Import $import */
$import = $this->fixtureModel->getObjectManager()->create(
'Magento\ImportExport\Model\Import',
- ['data' => ['entity' => 'catalog_product', 'behavior' => 'append']]
+ [
+ 'data' => [
+ 'entity' => 'catalog_product',
+ 'behavior' => 'append',
+ 'validation_strategy' => 'validation-stop-on-errors'
+ ]
+ ]
);
$source = new Generator($pattern, $configurablesCount);
diff --git a/setup/src/Magento/Setup/Fixtures/CustomersFixture.php b/setup/src/Magento/Setup/Fixtures/CustomersFixture.php
index c2390cd989164..2f65fe6f2301c 100644
--- a/setup/src/Magento/Setup/Fixtures/CustomersFixture.php
+++ b/setup/src/Magento/Setup/Fixtures/CustomersFixture.php
@@ -91,10 +91,16 @@ public function execute()
'_address_default_shipping_' => '1',
];
$generator = new Generator($pattern, $customersNumber);
- /** @var Magento\ImportExport\Model\Import $import */
+ /** @var \Magento\ImportExport\Model\Import $import */
$import = $this->fixtureModel->getObjectManager()->create(
'Magento\ImportExport\Model\Import',
- ['data' => ['entity' => 'customer_composite', 'behavior' => 'append']]
+ [
+ 'data' => [
+ 'entity' => 'customer_composite',
+ 'behavior' => 'append',
+ 'validation_strategy' => 'validation-stop-on-errors'
+ ]
+ ]
);
// it is not obvious, but the validateSource() will actually save import queue data to DB
$import->validateSource($generator);
diff --git a/setup/src/Magento/Setup/Fixtures/SimpleProductsFixture.php b/setup/src/Magento/Setup/Fixtures/SimpleProductsFixture.php
index ab977626ac4d6..0c60626964571 100644
--- a/setup/src/Magento/Setup/Fixtures/SimpleProductsFixture.php
+++ b/setup/src/Magento/Setup/Fixtures/SimpleProductsFixture.php
@@ -84,7 +84,13 @@ public function execute()
/** @var \Magento\ImportExport\Model\Import $import */
$import = $this->fixtureModel->getObjectManager()->create(
'Magento\ImportExport\Model\Import',
- ['data' => ['entity' => 'catalog_product', 'behavior' => 'append']]
+ [
+ 'data' => [
+ 'entity' => 'catalog_product',
+ 'behavior' => 'append',
+ 'validation_strategy' => 'validation-stop-on-errors'
+ ]
+ ]
);
// it is not obvious, but the validateSource() will actually save import queue data to DB
$import->validateSource($generator);
diff --git a/setup/src/Magento/Setup/Test/Unit/Fixtures/ConfigurableProductsFixtureTest.php b/setup/src/Magento/Setup/Test/Unit/Fixtures/ConfigurableProductsFixtureTest.php
index f0cd86010b237..0643f6cd40fec 100644
--- a/setup/src/Magento/Setup/Test/Unit/Fixtures/ConfigurableProductsFixtureTest.php
+++ b/setup/src/Magento/Setup/Test/Unit/Fixtures/ConfigurableProductsFixtureTest.php
@@ -80,7 +80,13 @@ public function testExecute()
$valueMap = [
[
'Magento\ImportExport\Model\Import',
- ['data' => ['entity' => 'catalog_product', 'behavior' => 'append']],
+ [
+ 'data' => [
+ 'entity' => 'catalog_product',
+ 'behavior' => 'append',
+ 'validation_strategy' => 'validation-stop-on-errors'
+ ]
+ ],
$importMock
],
['Magento\Store\Model\StoreManager', [], $storeManagerMock]
diff --git a/setup/src/Magento/Setup/Test/Unit/Fixtures/CustomersFixtureTest.php b/setup/src/Magento/Setup/Test/Unit/Fixtures/CustomersFixtureTest.php
index 6761be9e0dbd5..53d3fc8b63200 100644
--- a/setup/src/Magento/Setup/Test/Unit/Fixtures/CustomersFixtureTest.php
+++ b/setup/src/Magento/Setup/Test/Unit/Fixtures/CustomersFixtureTest.php
@@ -52,7 +52,13 @@ public function testExecute()
$valueMap = [
[
'Magento\ImportExport\Model\Import',
- ['data' => ['entity' => 'customer_composite', 'behavior' => 'append']],
+ [
+ 'data' => [
+ 'entity' => 'customer_composite',
+ 'behavior' => 'append',
+ 'validation_strategy' => 'validation-stop-on-errors'
+ ]
+ ],
$importMock
],
['Magento\Store\Model\StoreManager', [], $storeManagerMock]
diff --git a/setup/src/Magento/Setup/Test/Unit/Fixtures/SimpleProductsFixtureTest.php b/setup/src/Magento/Setup/Test/Unit/Fixtures/SimpleProductsFixtureTest.php
index 497a60768893c..163212dee20ef 100644
--- a/setup/src/Magento/Setup/Test/Unit/Fixtures/SimpleProductsFixtureTest.php
+++ b/setup/src/Magento/Setup/Test/Unit/Fixtures/SimpleProductsFixtureTest.php
@@ -80,7 +80,13 @@ public function testExecute()
$valueMap = [
[
'Magento\ImportExport\Model\Import',
- ['data' => ['entity' => 'catalog_product', 'behavior' => 'append']],
+ [
+ 'data' => [
+ 'entity' => 'catalog_product',
+ 'behavior' => 'append',
+ 'validation_strategy' => 'validation-stop-on-errors'
+ ]
+ ],
$importMock
],
['Magento\Store\Model\StoreManager', [], $storeManagerMock]