diff --git a/app/code/Magento/Catalog/Model/ProductRepository.php b/app/code/Magento/Catalog/Model/ProductRepository.php
index 83feea903f99343dae5f75dc01516945eae1e3d4..f260b01c02ef41d6b652c39750ed795c2f36e01f 100644
--- a/app/code/Magento/Catalog/Model/ProductRepository.php
+++ b/app/code/Magento/Catalog/Model/ProductRepository.php
@@ -6,10 +6,8 @@
  */
 namespace Magento\Catalog\Model;
 
-use Magento\Catalog\Api\Data\ProductInterface;
 use Magento\Catalog\Model\Product\Gallery\MimeTypeExtensionMap;
 use Magento\Catalog\Model\ResourceModel\Product\Collection;
-use Magento\Framework\Api\Data\ImageContentInterface;
 use Magento\Framework\Api\Data\ImageContentInterfaceFactory;
 use Magento\Framework\Api\ImageContentValidatorInterface;
 use Magento\Framework\Api\ImageProcessorInterface;
@@ -18,10 +16,8 @@ use Magento\Framework\DB\Adapter\ConnectionException;
 use Magento\Framework\DB\Adapter\DeadlockException;
 use Magento\Framework\DB\Adapter\LockWaitException;
 use Magento\Framework\Exception\CouldNotSaveException;
-use Magento\Framework\Exception\InputException;
 use Magento\Framework\Exception\LocalizedException;
 use Magento\Framework\Exception\NoSuchEntityException;
-use Magento\Framework\Exception\StateException;
 use Magento\Framework\Exception\ValidatorException;
 
 /**
@@ -116,11 +112,15 @@ class ProductRepository implements \Magento\Catalog\Api\ProductRepositoryInterfa
     protected $fileSystem;
 
     /**
+     * @deprecated
+     * @see \Magento\Catalog\Model\MediaGalleryProcessor
      * @var ImageContentInterfaceFactory
      */
     protected $contentFactory;
 
     /**
+     * @deprecated
+     * @see \Magento\Catalog\Model\MediaGalleryProcessor
      * @var ImageProcessorInterface
      */
     protected $imageProcessor;
@@ -131,7 +131,7 @@ class ProductRepository implements \Magento\Catalog\Api\ProductRepositoryInterfa
     protected $extensionAttributesJoinProcessor;
 
     /**
-     * @var \Magento\Catalog\Model\Product\Gallery\Processor
+     * @var ProductRepository\MediaGalleryProcessor
      */
     protected $mediaGalleryProcessor;
 
@@ -329,6 +329,9 @@ class ProductRepository implements \Magento\Catalog\Api\ProductRepositoryInterfa
         unset($productData['media_gallery']);
         if ($createNew) {
             $product = $this->productFactory->create();
+            if (isset($productData['price']) && !isset($productData['product_type'])) {
+                $product->setTypeId(Product\Type::TYPE_SIMPLE);
+            }
             if ($this->storeManager->hasSingleStore()) {
                 $product->setWebsiteIds([$this->storeManager->getStore(true)->getWebsiteId()]);
             }
@@ -375,53 +378,6 @@ class ProductRepository implements \Magento\Catalog\Api\ProductRepositoryInterfa
         $product->setWebsiteIds($websiteIds);
     }
 
-    /**
-     * @param ProductInterface $product
-     * @param array $newEntry
-     * @return $this
-     * @throws InputException
-     * @throws StateException
-     * @throws \Magento\Framework\Exception\LocalizedException
-     */
-    protected function processNewMediaGalleryEntry(
-        ProductInterface $product,
-        array  $newEntry
-    ) {
-        /** @var ImageContentInterface $contentDataObject */
-        $contentDataObject = $newEntry['content'];
-
-        /** @var \Magento\Catalog\Model\Product\Media\Config $mediaConfig */
-        $mediaConfig = $product->getMediaConfig();
-        $mediaTmpPath = $mediaConfig->getBaseTmpMediaPath();
-
-        $relativeFilePath = $this->imageProcessor->processImageContent($mediaTmpPath, $contentDataObject);
-        $tmpFilePath = $mediaConfig->getTmpMediaShortUrl($relativeFilePath);
-
-        if (!$product->hasGalleryAttribute()) {
-            throw new StateException(__('Requested product does not support images.'));
-        }
-
-        $imageFileUri = $this->getMediaGalleryProcessor()->addImage(
-            $product,
-            $tmpFilePath,
-            isset($newEntry['types']) ? $newEntry['types'] : [],
-            true,
-            isset($newEntry['disabled']) ? $newEntry['disabled'] : true
-        );
-        // Update additional fields that are still empty after addImage call
-        $this->getMediaGalleryProcessor()->updateImage(
-            $product,
-            $imageFileUri,
-            [
-                'label' => $newEntry['label'],
-                'position' => $newEntry['position'],
-                'disabled' => $newEntry['disabled'],
-                'media_type' => $newEntry['media_type'],
-            ]
-        );
-        return $this;
-    }
-
     /**
      * Process product links, creating new links, updating and deleting existing links
      *
@@ -480,67 +436,6 @@ class ProductRepository implements \Magento\Catalog\Api\ProductRepositoryInterfa
         return $this;
     }
 
-    /**
-     * Process Media gallery data before save product.
-     *
-     * Compare Media Gallery Entries Data with existing Media Gallery
-     * * If Media entry has not value_id set it as new
-     * * If Existing entry 'value_id' absent in Media Gallery set 'removed' flag
-     * * Merge Existing and new media gallery
-     *
-     * @param ProductInterface $product contains only existing media gallery items
-     * @param array $mediaGalleryEntries array which contains all media gallery items
-     * @return $this
-     * @throws InputException
-     * @throws StateException
-     * @throws LocalizedException
-     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
-     */
-    protected function processMediaGallery(ProductInterface $product, $mediaGalleryEntries)
-    {
-        $existingMediaGallery = $product->getMediaGallery('images');
-        $newEntries = [];
-        $entriesById = [];
-        if (!empty($existingMediaGallery)) {
-            foreach ($mediaGalleryEntries as $entry) {
-                if (isset($entry['id'])) {
-                    $entriesById[$entry['id']] = $entry;
-                } else {
-                    $newEntries[] = $entry;
-                }
-            }
-            foreach ($existingMediaGallery as $key => &$existingEntry) {
-                if (isset($entriesById[$existingEntry['value_id']])) {
-                    $updatedEntry = $entriesById[$existingEntry['value_id']];
-                    if (array_key_exists('file', $updatedEntry) && $updatedEntry['file'] === null) {
-                        unset($updatedEntry['file']);
-                    }
-                    $existingMediaGallery[$key] = array_merge($existingEntry, $updatedEntry);
-                } else {
-                    //set the removed flag
-                    $existingEntry['removed'] = true;
-                }
-            }
-            unset($existingEntry);
-            $product->setData('media_gallery', ["images" => $existingMediaGallery]);
-        } else {
-            $newEntries = $mediaGalleryEntries;
-        }
-
-        $this->getMediaGalleryProcessor()->clearMediaAttribute($product, array_keys($product->getMediaAttributes()));
-        $images = $product->getMediaGallery('images');
-        if ($images) {
-            foreach ($images as $image) {
-                if (!isset($image['removed']) && !empty($image['types'])) {
-                    $this->getMediaGalleryProcessor()->setMediaAttribute($product, $image['types'], $image['file']);
-                }
-            }
-        }
-        $this->processEntries($product, $newEntries, $entriesById);
-
-        return $this;
-    }
-
     /**
      * {@inheritdoc}
      * @SuppressWarnings(PHPMD.CyclomaticComplexity)
@@ -577,7 +472,10 @@ class ProductRepository implements \Magento\Catalog\Api\ProductRepositoryInterfa
 
         $this->processLinks($product, $productLinks);
         if (isset($productDataArray['media_gallery_entries'])) {
-            $this->processMediaGallery($product, $productDataArray['media_gallery_entries']);
+            $this->getMediaGalleryProcessor()->processMediaGallery(
+                $product,
+                $productDataArray['media_gallery_entries']
+            );
         }
 
         if (!$product->getOptionsReadonly()) {
@@ -749,13 +647,13 @@ class ProductRepository implements \Magento\Catalog\Api\ProductRepositoryInterfa
     }
 
     /**
-     * @return Product\Gallery\Processor
+     * @return ProductRepository\MediaGalleryProcessor
      */
     private function getMediaGalleryProcessor()
     {
         if (null === $this->mediaGalleryProcessor) {
             $this->mediaGalleryProcessor = \Magento\Framework\App\ObjectManager::getInstance()
-                ->get(\Magento\Catalog\Model\Product\Gallery\Processor::class);
+                ->get(ProductRepository\MediaGalleryProcessor::class);
         }
         return $this->mediaGalleryProcessor;
     }
@@ -775,60 +673,4 @@ class ProductRepository implements \Magento\Catalog\Api\ProductRepositoryInterfa
         }
         return $this->collectionProcessor;
     }
-
-    /**
-     * Convert extension attribute for product media gallery.
-     *
-     * @param array $newEntry
-     * @param array $extensionAttributes
-     * @return void
-     */
-    private function processExtensionAttributes(array &$newEntry, array $extensionAttributes)
-    {
-        foreach ($extensionAttributes as $code => $value) {
-            if (is_array($value)) {
-                $this->processExtensionAttributes($newEntry, $value);
-            } else {
-                $newEntry[$code] = $value;
-            }
-        }
-        unset($newEntry['extension_attributes']);
-    }
-
-    /**
-     * Convert entries into product media gallery data and set to product.
-     *
-     * @param ProductInterface $product
-     * @param array $newEntries
-     * @param array $entriesById
-     * @throws InputException
-     * @throws LocalizedException
-     * @throws StateException
-     * @return void
-     */
-    private function processEntries(ProductInterface $product, array $newEntries, array $entriesById)
-    {
-        foreach ($newEntries as $newEntry) {
-            if (!isset($newEntry['content'])) {
-                throw new InputException(__('The image content is not valid.'));
-            }
-            /** @var ImageContentInterface $contentDataObject */
-            $contentDataObject = $this->contentFactory->create()
-                ->setName($newEntry['content'][ImageContentInterface::NAME])
-                ->setBase64EncodedData($newEntry['content'][ImageContentInterface::BASE64_ENCODED_DATA])
-                ->setType($newEntry['content'][ImageContentInterface::TYPE]);
-            $newEntry['content'] = $contentDataObject;
-            $this->processNewMediaGalleryEntry($product, $newEntry);
-
-            $finalGallery = $product->getData('media_gallery');
-            $newEntryId = key(array_diff_key($product->getData('media_gallery')['images'], $entriesById));
-            if (isset($newEntry['extension_attributes'])) {
-                $this->processExtensionAttributes($newEntry, $newEntry['extension_attributes']);
-            }
-            $newEntry = array_replace_recursive($newEntry, $finalGallery['images'][$newEntryId]);
-            $entriesById[$newEntryId] = $newEntry;
-            $finalGallery['images'][$newEntryId] = $newEntry;
-            $product->setData('media_gallery', $finalGallery);
-        }
-    }
 }
diff --git a/app/code/Magento/Catalog/Model/ProductRepository/MediaGalleryProcessor.php b/app/code/Magento/Catalog/Model/ProductRepository/MediaGalleryProcessor.php
new file mode 100644
index 0000000000000000000000000000000000000000..4cc31d98fdfc2fd1d0d1ddc9487c0c5a1e301f6b
--- /dev/null
+++ b/app/code/Magento/Catalog/Model/ProductRepository/MediaGalleryProcessor.php
@@ -0,0 +1,218 @@
+<?php
+/**
+ *
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Catalog\Model\ProductRepository;
+
+use Magento\Catalog\Api\Data\ProductInterface;
+use Magento\Catalog\Model\Product\Gallery\Processor;
+use Magento\Framework\Api\Data\ImageContentInterface;
+use Magento\Framework\Api\Data\ImageContentInterfaceFactory;
+use Magento\Framework\Api\ImageProcessorInterface;
+use Magento\Framework\Exception\InputException;
+use Magento\Framework\Exception\LocalizedException;
+use Magento\Framework\Exception\StateException;
+
+/**
+ * Process Media gallery data for ProductRepository before save product.
+ */
+class MediaGalleryProcessor
+{
+    /**
+     * @var Processor
+     */
+    private $processor;
+
+    /**
+     * @var ImageContentInterfaceFactory
+     */
+    private $contentFactory;
+
+    /**
+     * @var ImageProcessorInterface
+     */
+    private $imageProcessor;
+
+    /**
+     * MediaGalleryProcessor constructor.
+     *
+     * @param Processor $processor
+     * @param ImageContentInterfaceFactory $contentFactory
+     * @param ImageProcessorInterface $imageProcessor
+     */
+    public function __construct(
+        Processor $processor,
+        ImageContentInterfaceFactory $contentFactory,
+        ImageProcessorInterface $imageProcessor
+    ) {
+        $this->processor = $processor;
+        $this->contentFactory = $contentFactory;
+        $this->imageProcessor = $imageProcessor;
+    }
+
+    /**
+     * Process Media gallery data before save product.
+     *
+     * Compare Media Gallery Entries Data with existing Media Gallery
+     * * If Media entry has not value_id set it as new
+     * * If Existing entry 'value_id' absent in Media Gallery set 'removed' flag
+     * * Merge Existing and new media gallery
+     *
+     * @param ProductInterface $product contains only existing media gallery items.
+     * @param array $mediaGalleryEntries array which contains all media gallery items.
+     * @return void
+     * @throws InputException
+     * @throws StateException
+     * @throws LocalizedException
+     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
+     */
+    public function processMediaGallery(ProductInterface $product, array $mediaGalleryEntries)
+    {
+        $existingMediaGallery = $product->getMediaGallery('images');
+        $newEntries = [];
+        $entriesById = [];
+        if (!empty($existingMediaGallery)) {
+            foreach ($mediaGalleryEntries as $entry) {
+                if (isset($entry['id'])) {
+                    $entriesById[$entry['id']] = $entry;
+                } else {
+                    $newEntries[] = $entry;
+                }
+            }
+            foreach ($existingMediaGallery as $key => &$existingEntry) {
+                if (isset($entriesById[$existingEntry['value_id']])) {
+                    $updatedEntry = $entriesById[$existingEntry['value_id']];
+                    if (array_key_exists('file', $updatedEntry) && $updatedEntry['file'] === null) {
+                        unset($updatedEntry['file']);
+                    }
+                    $existingMediaGallery[$key] = array_merge($existingEntry, $updatedEntry);
+                } else {
+                    //set the removed flag.
+                    $existingEntry['removed'] = true;
+                }
+            }
+            unset($existingEntry);
+            $product->setData('media_gallery', ["images" => $existingMediaGallery]);
+        } else {
+            $newEntries = $mediaGalleryEntries;
+        }
+
+        $this->processor->clearMediaAttribute($product, array_keys($product->getMediaAttributes()));
+        $images = $product->getMediaGallery('images');
+        if ($images) {
+            foreach ($images as $image) {
+                if (!isset($image['removed']) && !empty($image['types'])) {
+                    $this->processor->setMediaAttribute($product, $image['types'], $image['file']);
+                }
+            }
+        }
+        $this->processEntries($product, $newEntries, $entriesById);
+    }
+
+    /**
+     * Convert entries into product media gallery data and set to product.
+     *
+     * @param ProductInterface $product
+     * @param array $newEntries
+     * @param array $entriesById
+     * @throws InputException
+     * @throws LocalizedException
+     * @throws StateException
+     * @return void
+     */
+    private function processEntries(ProductInterface $product, array $newEntries, array $entriesById)
+    {
+        foreach ($newEntries as $newEntry) {
+            if (!isset($newEntry['content'])) {
+                throw new InputException(__('The image content is not valid.'));
+            }
+            /** @var ImageContentInterface $contentDataObject */
+            $contentDataObject = $this->contentFactory->create()
+                ->setName($newEntry['content'][ImageContentInterface::NAME])
+                ->setBase64EncodedData($newEntry['content'][ImageContentInterface::BASE64_ENCODED_DATA])
+                ->setType($newEntry['content'][ImageContentInterface::TYPE]);
+            $newEntry['content'] = $contentDataObject;
+            $this->processNewMediaGalleryEntry($product, $newEntry);
+
+            $finalGallery = $product->getData('media_gallery');
+            $newEntryId = key(array_diff_key($product->getData('media_gallery')['images'], $entriesById));
+            if (isset($newEntry['extension_attributes'])) {
+                $this->processExtensionAttributes($newEntry, $newEntry['extension_attributes']);
+            }
+            $newEntry = array_replace_recursive($newEntry, $finalGallery['images'][$newEntryId]);
+            $entriesById[$newEntryId] = $newEntry;
+            $finalGallery['images'][$newEntryId] = $newEntry;
+            $product->setData('media_gallery', $finalGallery);
+        }
+    }
+
+    /**
+     * Save gallery entry as image.
+     *
+     * @param ProductInterface $product
+     * @param array $newEntry
+     * @return void
+     * @throws InputException
+     * @throws StateException
+     * @throws \Magento\Framework\Exception\LocalizedException
+     */
+    private function processNewMediaGalleryEntry(
+        ProductInterface $product,
+        array $newEntry
+    ) {
+        /** @var ImageContentInterface $contentDataObject */
+        $contentDataObject = $newEntry['content'];
+
+        /** @var \Magento\Catalog\Model\Product\Media\Config $mediaConfig */
+        $mediaConfig = $product->getMediaConfig();
+        $mediaTmpPath = $mediaConfig->getBaseTmpMediaPath();
+
+        $relativeFilePath = $this->imageProcessor->processImageContent($mediaTmpPath, $contentDataObject);
+        $tmpFilePath = $mediaConfig->getTmpMediaShortUrl($relativeFilePath);
+
+        if (!$product->hasGalleryAttribute()) {
+            throw new StateException(__('Requested product does not support images.'));
+        }
+
+        $imageFileUri = $this->processor->addImage(
+            $product,
+            $tmpFilePath,
+            isset($newEntry['types']) ? $newEntry['types'] : [],
+            true,
+            isset($newEntry['disabled']) ? $newEntry['disabled'] : true
+        );
+        // Update additional fields that are still empty after addImage call.
+        $this->processor->updateImage(
+            $product,
+            $imageFileUri,
+            [
+                'label' => $newEntry['label'],
+                'position' => $newEntry['position'],
+                'disabled' => $newEntry['disabled'],
+                'media_type' => $newEntry['media_type'],
+            ]
+        );
+    }
+
+    /**
+     * Convert extension attribute for product media gallery.
+     *
+     * @param array $newEntry
+     * @param array $extensionAttributes
+     * @return void
+     */
+    private function processExtensionAttributes(array &$newEntry, array $extensionAttributes)
+    {
+        foreach ($extensionAttributes as $code => $value) {
+            if (is_array($value)) {
+                $this->processExtensionAttributes($newEntry, $value);
+            } else {
+                $newEntry[$code] = $value;
+            }
+        }
+        unset($newEntry['extension_attributes']);
+    }
+}
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ProductRepository/MediaGalleryProcessorTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ProductRepository/MediaGalleryProcessorTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..02773b2fb3d703e507101ca3ad899c53fdacc84f
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Unit/Model/ProductRepository/MediaGalleryProcessorTest.php
@@ -0,0 +1,227 @@
+<?php
+/**
+ *
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Catalog\Test\Unit\Model\ProductRepository;
+
+use Magento\Catalog\Model\Product;
+use Magento\Catalog\Model\Product\Gallery\Processor;
+use Magento\Catalog\Model\ProductRepository\MediaGalleryProcessor;
+use Magento\Framework\Api\Data\ImageContentInterface;
+use Magento\Framework\Api\Data\ImageContentInterfaceFactory;
+use Magento\Framework\Api\ImageProcessorInterface;
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
+use PHPUnit\Framework\TestCase;
+
+/**
+ * Provide tests for ProductRepository/MediaGalleryProcessor.
+ */
+class MediaGalleryProcessorTest extends TestCase
+{
+    /**
+     * Test subject.
+     *
+     * @var MediaGalleryProcessor
+     */
+    private $model;
+
+    /**
+     * @var Processor|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $processor;
+
+    /**
+     * @var ImageContentInterfaceFactory|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $contentFactory;
+
+    /**
+     * @var ImageProcessorInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $imageProcessor;
+
+    /**
+     * @var Product|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $product;
+
+    /**
+     * @inheritdoc
+     */
+    protected function setUp()
+    {
+        $this->product = $this->createPartialMock(
+            \Magento\Catalog\Model\Product::class,
+            [
+                'hasGalleryAttribute',
+                'getMediaConfig',
+                'getMediaAttributes',
+                'getMediaGalleryEntries',
+            ]
+        );
+        $this->product->expects($this->any())
+            ->method('hasGalleryAttribute')
+            ->willReturn(true);
+        $this->processor = $this->getMockBuilder(Processor::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->contentFactory = $this->getMockBuilder(ImageContentInterfaceFactory::class)
+            ->setMethods(['create'])
+            ->disableOriginalConstructor()
+            ->getMockForAbstractClass();
+        $this->imageProcessor = $this->getMockBuilder(ImageProcessorInterface::class)
+            ->disableOriginalConstructor()
+            ->getMockForAbstractClass();
+        $objectManager = new ObjectManager($this);
+        $this->model = $objectManager->getObject(
+            MediaGalleryProcessor::class,
+            [
+                'processor' => $this->processor,
+                'contentFactory' => $this->contentFactory,
+                'imageProcessor' => $this->imageProcessor,
+            ]
+        );
+    }
+
+    /**
+     * Test add image.
+     *
+     * @return void
+     */
+    public function testProcessWithNewMediaEntry()
+    {
+        $mediaGalleryEntries = [
+            [
+                'value_id' => null,
+                'label' => 'label_text',
+                'position' => 10,
+                'disabled' => false,
+                'types' => ['image', 'small_image'],
+                'content' => [
+                    ImageContentInterface::NAME => 'filename',
+                    ImageContentInterface::TYPE => 'image/jpeg',
+                    ImageContentInterface::BASE64_ENCODED_DATA => 'encoded_content',
+                ],
+                'media_type' => 'media_type',
+            ],
+        ];
+
+        //setup media attribute backend.
+        $mediaTmpPath = '/tmp';
+        $absolutePath = '/a/b/filename.jpg';
+        $mediaConfigMock = $this->getMockBuilder(\Magento\Catalog\Model\Product\Media\Config::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $mediaConfigMock->expects($this->once())
+            ->method('getTmpMediaShortUrl')
+            ->with($absolutePath)
+            ->willReturn($mediaTmpPath . $absolutePath);
+        $this->product->setData('media_gallery', ['images' => $mediaGalleryEntries]);
+        $this->product->expects($this->any())
+            ->method('getMediaAttributes')
+            ->willReturn(['image' => 'imageAttribute', 'small_image' => 'small_image_attribute']);
+        $this->product->expects($this->once())
+            ->method('getMediaConfig')
+            ->willReturn($mediaConfigMock);
+        $this->processor->expects($this->once())->method('clearMediaAttribute')
+            ->with($this->product, ['image', 'small_image']);
+
+        //verify new entries.
+        $contentDataObject = $this->getMockBuilder(\Magento\Framework\Api\ImageContent::class)
+            ->disableOriginalConstructor()
+            ->setMethods(null)
+            ->getMock();
+        $this->contentFactory->expects($this->once())
+            ->method('create')
+            ->willReturn($contentDataObject);
+
+        $this->imageProcessor->expects($this->once())
+            ->method('processImageContent')
+            ->willReturn($absolutePath);
+
+        $imageFileUri = 'imageFileUri';
+        $this->processor->expects($this->once())->method('addImage')
+            ->with($this->product, $mediaTmpPath . $absolutePath, ['image', 'small_image'], true, false)
+            ->willReturn($imageFileUri);
+        $this->processor->expects($this->once())->method('updateImage')
+            ->with(
+                $this->product,
+                $imageFileUri,
+                [
+                    'label' => 'label_text',
+                    'position' => 10,
+                    'disabled' => false,
+                    'media_type' => 'media_type',
+                ]
+            );
+
+        $this->model->processMediaGallery($this->product, $mediaGalleryEntries);
+    }
+
+    /**
+     * Test update(delete) images.
+     */
+    public function testProcessExistingWithMediaGalleryEntries()
+    {
+        //update one entry, delete one entry.
+        $newEntries = [
+            [
+                'id' => 5,
+                'label' => 'new_label_text',
+                'file' => 'filename1',
+                'position' => 10,
+                'disabled' => false,
+                'types' => ['image', 'small_image'],
+            ],
+        ];
+
+        $existingMediaGallery = [
+            'images' => [
+                [
+                    'value_id' => 5,
+                    'label' => 'label_text',
+                    'file' => 'filename1',
+                    'position' => 10,
+                    'disabled' => true,
+                ],
+                [
+                    'value_id' => 6, //will be deleted.
+                    'file' => 'filename2',
+                ],
+            ],
+        ];
+
+        $expectedResult = [
+            [
+                'value_id' => 5,
+                'id' => 5,
+                'label' => 'new_label_text',
+                'file' => 'filename1',
+                'position' => 10,
+                'disabled' => false,
+                'types' => ['image', 'small_image'],
+            ],
+            [
+                'value_id' => 6, //will be deleted.
+                'file' => 'filename2',
+                'removed' => true,
+            ],
+        ];
+
+        $this->product->setData('media_gallery', $existingMediaGallery);
+        $this->product->expects($this->any())
+            ->method('getMediaAttributes')
+            ->willReturn(['image' => 'filename1', 'small_image' => 'filename2']);
+
+        $this->processor->expects($this->once())->method('clearMediaAttribute')
+            ->with($this->product, ['image', 'small_image']);
+        $this->processor->expects($this->once())
+            ->method('setMediaAttribute')
+            ->with($this->product, ['image', 'small_image'], 'filename1');
+        $this->model->processMediaGallery($this->product, $newEntries);
+        $this->assertEquals($expectedResult, $this->product->getMediaGallery('images'));
+    }
+}
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php
index a220b9a5768fed1b0d043cb7d1629439e657fc7d..14c84f4781a3aa962dbbf04f2ec60d1e11954915 100644
--- a/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php
@@ -9,6 +9,7 @@
 
 namespace Magento\Catalog\Test\Unit\Model;
 
+use Magento\Catalog\Model\ProductRepository\MediaGalleryProcessor;
 use Magento\Framework\Api\Data\ImageContentInterface;
 use Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface;
 use Magento\Framework\DB\Adapter\ConnectionException;
@@ -139,7 +140,7 @@ class ProductRepositoryTest extends \PHPUnit\Framework\TestCase
     protected $storeManagerMock;
 
     /**
-     * @var \Magento\Catalog\Model\Product\Gallery\Processor|\PHPUnit_Framework_MockObject_MockObject
+     * @var MediaGalleryProcessor|\PHPUnit_Framework_MockObject_MockObject
      */
     protected $mediaGalleryProcessor;
 
@@ -234,7 +235,7 @@ class ProductRepositoryTest extends \PHPUnit\Framework\TestCase
         $storeMock->expects($this->any())->method('getCode')->willReturn(\Magento\Store\Model\Store::ADMIN_CODE);
         $this->storeManagerMock->expects($this->any())->method('getStore')->willReturn($storeMock);
 
-        $this->mediaGalleryProcessor = $this->createMock(\Magento\Catalog\Model\Product\Gallery\Processor::class);
+        $this->mediaGalleryProcessor = $this->createMock(MediaGalleryProcessor::class);
 
         $this->collectionProcessorMock = $this->getMockBuilder(CollectionProcessorInterface::class)
             ->getMock();
@@ -1174,7 +1175,21 @@ class ProductRepositoryTest extends \PHPUnit\Framework\TestCase
                     ]
                 ]
         ];
-
+        $expectedEntriesData = [
+            [
+                'id' => null,
+                'label' => "label_text",
+                'position' => 10,
+                'disabled' => false,
+                'types' => ['image', 'small_image'],
+                'content' => [
+                    ImageContentInterface::NAME => 'filename',
+                    ImageContentInterface::TYPE => 'image/jpeg',
+                    ImageContentInterface::BASE64_ENCODED_DATA => 'encoded_content',
+                ],
+                'media_type' => 'media_type',
+            ],
+        ];
         $this->setupProductMocksForSave();
         //media gallery data
         $this->productData['media_gallery_entries'] = [
@@ -1198,56 +1213,8 @@ class ProductRepositoryTest extends \PHPUnit\Framework\TestCase
             ->will($this->returnValue($this->productData));
 
         $this->initializedProductMock->setData('media_gallery', $newEntriesData);
-        $this->initializedProductMock->expects($this->any())
-            ->method('getMediaAttributes')
-            ->willReturn(["image" => "imageAttribute", "small_image" => "small_image_attribute"]);
-
-        //setup media attribute backend
-        $mediaTmpPath = '/tmp';
-        $absolutePath = '/a/b/filename.jpg';
-
-        $this->mediaGalleryProcessor->expects($this->once())->method('clearMediaAttribute')
-            ->with($this->initializedProductMock, ['image', 'small_image']);
-
-        $mediaConfigMock = $this->getMockBuilder(\Magento\Catalog\Model\Product\Media\Config::class)
-            ->disableOriginalConstructor()
-            ->getMock();
-        $mediaConfigMock->expects($this->once())
-            ->method('getTmpMediaShortUrl')
-            ->with($absolutePath)
-            ->willReturn($mediaTmpPath . $absolutePath);
-        $this->initializedProductMock->expects($this->once())
-            ->method('getMediaConfig')
-            ->willReturn($mediaConfigMock);
-
-        //verify new entries
-        $contentDataObject = $this->getMockBuilder(\Magento\Framework\Api\ImageContent::class)
-            ->disableOriginalConstructor()
-            ->setMethods(null)
-            ->getMock();
-        $this->contentFactoryMock->expects($this->once())
-            ->method('create')
-            ->willReturn($contentDataObject);
-
-        $this->imageProcessorMock->expects($this->once())
-            ->method('processImageContent')
-            ->willReturn($absolutePath);
-
-        $imageFileUri = "imageFileUri";
-        $this->mediaGalleryProcessor->expects($this->once())->method('addImage')
-            ->with($this->initializedProductMock, $mediaTmpPath . $absolutePath, ['image', 'small_image'], true, false)
-            ->willReturn($imageFileUri);
-        $this->mediaGalleryProcessor->expects($this->once())->method('updateImage')
-            ->with(
-                $this->initializedProductMock,
-                $imageFileUri,
-                [
-                    'label' => 'label_text',
-                    'position' => 10,
-                    'disabled' => false,
-                    'media_type' => 'media_type',
-                ]
-            );
+        $this->mediaGalleryProcessor->expects($this->once())->method('processMediaGallery')
+            ->with($this->initializedProductMock, $expectedEntriesData);
         $this->initializedProductMock->expects($this->once())->method('getWebsiteIds')->willReturn([]);
         $this->initializedProductMock->expects($this->atLeastOnce())
             ->method('getSku')->willReturn($this->productData['sku']);
@@ -1325,24 +1292,6 @@ class ProductRepositoryTest extends \PHPUnit\Framework\TestCase
                 ],
             ],
         ];
-
-        $expectedResult = [
-            [
-                'value_id' => 5,
-                'id' => 5,
-                "label" => "new_label_text",
-                'file' => 'filename1',
-                'position' => 10,
-                'disabled' => false,
-                'types' => ['image', 'small_image'],
-            ],
-            [
-                'value_id' => 6, //will be deleted
-                'file' => 'filename2',
-                'removed' => true,
-            ],
-        ];
-
         $this->setupProductMocksForSave();
         //media gallery data
         $this->productData['media_gallery_entries'] = $newEntries;
@@ -1352,21 +1301,15 @@ class ProductRepositoryTest extends \PHPUnit\Framework\TestCase
             ->will($this->returnValue($this->productData));
 
         $this->initializedProductMock->setData('media_gallery', $existingMediaGallery);
-        $this->initializedProductMock->expects($this->any())
-            ->method('getMediaAttributes')
-            ->willReturn(["image" => "filename1", "small_image" => "filename2"]);
 
-        $this->mediaGalleryProcessor->expects($this->once())->method('clearMediaAttribute')
-            ->with($this->initializedProductMock, ['image', 'small_image']);
         $this->mediaGalleryProcessor->expects($this->once())
-            ->method('setMediaAttribute')
-            ->with($this->initializedProductMock, ['image', 'small_image'], 'filename1');
+            ->method('processMediaGallery')
+            ->with($this->initializedProductMock, $newEntries);
         $this->initializedProductMock->expects($this->once())->method('getWebsiteIds')->willReturn([]);
         $this->initializedProductMock->expects($this->atLeastOnce())
             ->method('getSku')->willReturn($this->productData['sku']);
         $this->productMock->expects($this->atLeastOnce())->method('getSku')->willReturn($this->productData['sku']);
         $this->productMock->expects($this->any())->method('getMediaGalleryEntries')->willReturn(null);
         $this->model->save($this->productMock);
-        $this->assertEquals($expectedResult, $this->initializedProductMock->getMediaGallery('images'));
     }
 }
diff --git a/app/code/Magento/CatalogInventory/etc/events.xml b/app/code/Magento/CatalogInventory/etc/events.xml
index 0a9f3c2d40dcad6495d77ef2269f20b72d48da4e..3197501e9b70b1f410f4765c2282276dfceed924 100644
--- a/app/code/Magento/CatalogInventory/etc/events.xml
+++ b/app/code/Magento/CatalogInventory/etc/events.xml
@@ -27,9 +27,6 @@
     <event name="sales_model_service_quote_submit_failure">
         <observer name="inventory" instance="Magento\CatalogInventory\Observer\RevertQuoteInventoryObserver"/>
     </event>
-    <event name="restore_quote">
-        <observer name="inventory" instance="Magento\CatalogInventory\Observer\RevertQuoteInventoryObserver"/>
-    </event>
     <event name="sales_order_item_cancel">
         <observer name="inventory" instance="Magento\CatalogInventory\Observer\CancelOrderItemObserver"/>
     </event>
diff --git a/app/code/Magento/ConfigurableProductSales/Model/Order/Reorder/OrderedProductAvailabilityChecker.php b/app/code/Magento/ConfigurableProductSales/Model/Order/Reorder/OrderedProductAvailabilityChecker.php
index dceb5767edae91ce12db919370c0ea0a38c33d0d..42d7d91fb90e85785acdb3f6a53f1d37f6e680a6 100644
--- a/app/code/Magento/ConfigurableProductSales/Model/Order/Reorder/OrderedProductAvailabilityChecker.php
+++ b/app/code/Magento/ConfigurableProductSales/Model/Order/Reorder/OrderedProductAvailabilityChecker.php
@@ -45,7 +45,7 @@ class OrderedProductAvailabilityChecker implements OrderedProductAvailabilityChe
     public function isAvailable(Item $item)
     {
         $buyRequest = $item->getBuyRequest();
-        $superAttribute = $buyRequest->getData()['super_attribute'];
+        $superAttribute = $buyRequest->getData()['super_attribute'] ?? [];
         $connection = $this->getConnection();
         $select = $connection->select();
         $orderItemParentId = $item->getParentItem()->getProductId();
diff --git a/app/code/Magento/GoogleAnalytics/view/frontend/web/js/google-analytics.js b/app/code/Magento/GoogleAnalytics/view/frontend/web/js/google-analytics.js
index 324881cdc502821ea4251d43bc0ef24abce95d1c..cd6292b39e98921e649b3afc4bb3965063129725 100644
--- a/app/code/Magento/GoogleAnalytics/view/frontend/web/js/google-analytics.js
+++ b/app/code/Magento/GoogleAnalytics/view/frontend/web/js/google-analytics.js
@@ -51,10 +51,9 @@ define([
             if (config.pageTrackingData.isAnonymizedIpActive) {
                 ga('set', 'anonymizeIp', true);
             }
-            ga('send', 'pageview' + config.pageTrackingData.optPageUrl);
 
             // Process orders data
-            if (config.ordersTrackingData) {
+            if (config.ordersTrackingData.length) {
                 ga('require', 'ec', 'ec.js');
 
                 //Set currency code
@@ -75,6 +74,9 @@ define([
                 }
 
                 ga('send', 'pageview');
+            }else{
+                // Process Data if not orders
+                ga('send', 'pageview' + config.pageTrackingData.optPageUrl);
             }
         }
     }
diff --git a/app/code/Magento/Newsletter/Model/Subscriber.php b/app/code/Magento/Newsletter/Model/Subscriber.php
index b8d7ccf83af7ccc29a6e322e769cd39b267030d5..8f29798472f19c11178f4480c5272d2b4638b55d 100644
--- a/app/code/Magento/Newsletter/Model/Subscriber.php
+++ b/app/code/Magento/Newsletter/Model/Subscriber.php
@@ -604,14 +604,20 @@ class Subscriber extends \Magento\Framework\Model\AbstractModel
 
         $this->save();
         $sendSubscription = $sendInformationEmail;
-        if ($sendSubscription === null xor $sendSubscription) {
+        if ($sendSubscription === null xor $sendSubscription && $this->isStatusChanged()) {
             try {
-                if ($isConfirmNeed) {
-                    $this->sendConfirmationRequestEmail();
-                } elseif ($this->isStatusChanged() && $status == self::STATUS_UNSUBSCRIBED) {
-                    $this->sendUnsubscriptionEmail();
-                } elseif ($this->isStatusChanged() && $status == self::STATUS_SUBSCRIBED) {
-                    $this->sendConfirmationSuccessEmail();
+                switch ($status) {
+                    case self::STATUS_UNSUBSCRIBED:
+                        $this->sendUnsubscriptionEmail();
+                        break;
+                    case self::STATUS_SUBSCRIBED:
+                        $this->sendConfirmationSuccessEmail();
+                        break;
+                    case self::STATUS_NOT_ACTIVE:
+                        if ($isConfirmNeed) {
+                            $this->sendConfirmationRequestEmail();
+                        }
+                        break;
                 }
             } catch (MailException $e) {
                 // If we are not able to send a new account email, this should be ignored
diff --git a/app/code/Magento/Quote/Model/Quote/Item.php b/app/code/Magento/Quote/Model/Quote/Item.php
index d8177ddfe5236500cb5d2099bfab2b6d47ee9391..fe6d712500bcd7e6d61a782fe757dc78f67c96e5 100644
--- a/app/code/Magento/Quote/Model/Quote/Item.php
+++ b/app/code/Magento/Quote/Model/Quote/Item.php
@@ -745,6 +745,9 @@ class Item extends \Magento\Quote\Model\Quote\Item\AbstractItem implements \Mage
                 unset($this->_options[$index]);
                 unset($this->_optionsByCode[$option->getCode()]);
             } else {
+                if (!$option->getItem() || !$option->getItem()->getId()) {
+                    $option->setItem($this);
+                }
                 $option->save();
             }
         }
diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php
index 09f6362c833d4de773bc6702fc0f5a28c614b473..9dab97621f2f54de8da46ab592a3f79fb765b2d0 100644
--- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php
@@ -326,6 +326,32 @@ class ProductRepositoryInterfaceTest extends WebapiAbstract
         }
     }
 
+    /**
+     * Test that Product Repository can correctly create simple product, if product type not specified in request.
+     *
+     * @return void
+     */
+    public function testCreateWithoutSpecifiedType()
+    {
+        $price = 3.62;
+        $weight = 12.2;
+        $sku = 'simple_product_without_specified_type';
+        $product = [
+            'sku' => $sku,
+            'name' => 'Simple Product Without Specified Type',
+            'price' => $price,
+            'weight' => $weight,
+            'attribute_set_id' => 4,
+        ];
+        $response = $this->saveProduct($product);
+        $this->assertSame($sku, $response[ProductInterface::SKU]);
+        $this->assertSame(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE, $response[ProductInterface::TYPE_ID]);
+        $this->assertSame($price, $response[ProductInterface::PRICE]);
+        $this->assertSame($weight, $response[ProductInterface::WEIGHT]);
+        //Clean up.
+        $this->deleteProduct($product[ProductInterface::SKU]);
+    }
+
     /**
      * @param array $fixtureProduct
      *
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductRepositoryTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductRepositoryTest.php
index 9518e9c0cdf4fbf476e2cf554ce566afc3547714..330487b757f617bec675e0a87ddba0a9c99049e1 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductRepositoryTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductRepositoryTest.php
@@ -49,4 +49,25 @@ class ProductRepositoryTest extends \PHPUnit\Framework\TestCase
         $updatedProduct->load($productId);
         self::assertSame($newSku, $updatedProduct->getSku());
     }
+
+    /**
+     * Check Product Repository able to correctly create product without specified type.
+     *
+     * @magentoDbIsolation enabled
+     */
+    public function testCreateWithoutSpecifiedType()
+    {
+        /** @var Product $product */
+        $product = Bootstrap::getObjectManager()->get(ProductFactory::class)->create();
+        $product->setName('Simple without specified type');
+        $product->setSku('simple_without_specified_type');
+        $product->setPrice(1.12);
+        $product->setWeight(1.23);
+        $product->setAttributeSetId(4);
+        $product = $this->productRepository->save($product);
+
+        self::assertSame('1.1200', $product->getPrice());
+        self::assertSame('1.2300', $product->getWeight());
+        self::assertSame('simple', $product->getTypeId());
+    }
 }