Skip to content

Commit

Permalink
MDEE-590 - Disable Update On Save (realtime) mode for exporter indexe…
Browse files Browse the repository at this point in the history
…rs (#352)

* MDEE-590: Force exporter indexer to be in "update on schedule" mode
  • Loading branch information
virfaure authored Dec 5, 2023
1 parent bc5211e commit 3152e42
Show file tree
Hide file tree
Showing 30 changed files with 825 additions and 326 deletions.
11 changes: 7 additions & 4 deletions BundleProductDataExporter/Test/Integration/BundleProductTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
*/
class BundleProductTest extends AbstractProductTestHelper
{
private const BUNDLE_SKU = 'bundle-product';
private const DYNAMIC_BUNDLE_SKU = 'dynamic_bundle_product_with_special_price';

/**
* @var ArrayUtils
*/
Expand Down Expand Up @@ -47,7 +50,7 @@ protected function setUp() : void
*/
public function testBundleFixedProductOptions(array $bundleProductOptionsDataProvider) : void
{
$extractedProduct = $this->getExtractedProduct('bundle-product', 'default');
$extractedProduct = $this->getExtractedProduct(self::BUNDLE_SKU, 'default');
$this->assertNotEmpty($extractedProduct, 'Feed data must not be empty');

foreach ($bundleProductOptionsDataProvider as $key => $expectedData) {
Expand All @@ -72,7 +75,7 @@ public function testBundleFixedProductOptions(array $bundleProductOptionsDataPro
*/
public function testBundleDynamicProductOptions(array $bundleProductOptionsDataProvider) : void
{
$extractedProduct = $this->getExtractedProduct('dynamic_bundle_product_with_special_price', 'default');
$extractedProduct = $this->getExtractedProduct(self::DYNAMIC_BUNDLE_SKU, 'default');
$this->assertNotEmpty($extractedProduct, 'Feed data must not be empty');

foreach ($bundleProductOptionsDataProvider as $key => $expectedData) {
Expand All @@ -92,7 +95,7 @@ public function getBundleFixedProductOptionsDataProvider() : array
'bundleProduct' => [
'item' => [
'feedData' => [
'sku' => 'bundle-product',
'sku' => self::BUNDLE_SKU,
'storeViewCode' => 'default',
'name' => 'Bundle Product',
'type' => 'bundle_fixed',
Expand Down Expand Up @@ -134,7 +137,7 @@ public function getBundleDynamicProductOptionsDataProvider() : array
'bundleProduct' => [
'item' => [
'feedData' => [
'sku' => 'dynamic_bundle_product_with_special_price',
'sku' => self::DYNAMIC_BUNDLE_SKU,
'storeViewCode' => 'default',
'name' => 'Bundle Product',
'type' => 'bundle',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,12 @@

namespace Magento\BundleProductDataExporter\Test\Integration;

use DateTime;
use DateTimeInterface;
use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Framework\App\ResourceConnection;
use Magento\CatalogDataExporter\Test\Integration\AbstractProductTestHelper;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\DataExporter\Model\FeedInterface;
use Magento\DataExporter\Model\FeedPool;
use Magento\Indexer\Model\Indexer;
use Magento\TestFramework\Helper\Bootstrap;
use PHPUnit\Framework\TestCase;
use RuntimeException;
use Throwable;
use Zend_Db_Statement_Exception;

/**
Expand All @@ -29,45 +23,22 @@
* @SuppressWarnings(PHPMD.UnusedPrivateMethod)
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class ExportBundleOptionWithParentTest extends TestCase
class ExportBundleOptionWithParentTest extends AbstractProductTestHelper
{
private const PRODUCT_FEED_INDEXER = 'catalog_data_exporter_products';

/**
* @var Indexer
*/
private Indexer $indexer;

private const SIMPLE_SKU = 'simple1';
/**
* @var FeedInterface
*/
private FeedInterface $productsFeed;

/**
* @var ProductRepositoryInterface
* @inheritDoc
*/
private ProductRepositoryInterface $productRepository;

/**
* @var ResourceConnection
*/
private ResourceConnection $resourceConnection;

/**
* @param string|null $name
* @param array $data
* @param $dataName
*/
public function __construct(
?string $name = null,
array $data = [],
$dataName = ''
) {
parent::__construct($name, $data, $dataName);
$this->indexer = Bootstrap::getObjectManager()->create(Indexer::class);
$this->productRepository = Bootstrap::getObjectManager()->create(ProductRepositoryInterface::class);
protected function setUp() : void
{
$this->productsFeed = Bootstrap::getObjectManager()->get(FeedPool::class)->getFeed('products');
$this->resourceConnection = Bootstrap::getObjectManager()->create(ResourceConnection::class);

parent::setUp();
}

/**
Expand All @@ -92,7 +63,7 @@ private function expectedBundleOptionsWithParentData(): array
[
[
[
'sku' => 'simple1',
'sku' => self::SIMPLE_SKU,
'type' => 'SIMPLE',
'parents' => [
0 => ['sku' => 'bundle-product', 'productType' => 'bundle_fixed'],
Expand All @@ -112,66 +83,18 @@ private function expectedBundleOptionsWithParentData(): array
*/
private function checkExpectedItemsAreExportedInFeed(array $expectedItems): void
{
$ids = [];
foreach ($expectedItems as $expectedItem) {
$ids[] = $this->productRepository->get($expectedItem['sku'])->getId();
}
$timestamp = new DateTime('Now - 1 second');
$this->runIndexer($ids);
$actualProductsFeed = $this->productsFeed->getFeedSince($timestamp->format(DateTimeInterface::W3C));
$extractedProduct = $this->getExtractedProduct(self::SIMPLE_SKU, 'default');

self::assertNotEmpty($actualProductsFeed['feed'], 'Product Feed should not be empty');
self::assertNotEmpty($extractedProduct, 'Product Feed should not be empty');

foreach ($expectedItems as $index => $product) {
if (!isset($actualProductsFeed['feed'][$index])) {
self::fail("Cannot find product feed");
}

self::assertEquals(
$product['sku'],
$actualProductsFeed['feed'][$index]['sku'],
"Sku is not equal for index {$index}"
);
foreach ($expectedItems as $product) {
self::assertEquals($product['sku'], $extractedProduct['sku']);

self::assertEqualsCanonicalizing(
$product['parents'],
$actualProductsFeed['feed'][$index]['parents'],
"Parents is not equal"
$extractedProduct['feedData']['parents'],
"Expected Parents are not equal to Actual"
);
}
}

/**
* Run the indexer to extract product data
* @param $ids
* @return void
*/
private function runIndexer($ids): void
{
try {
$this->indexer->load(self::PRODUCT_FEED_INDEXER);
$this->indexer->reindexList($ids);
} catch (Throwable) {
throw new RuntimeException('Could not reindex product data');
}
}

/**
* @return void
*/
protected function tearDown(): void
{
parent::tearDown();
$this->truncateIndexTable();
}

/**
* Truncates index table
*/
private function truncateIndexTable(): void
{
$connection = $this->resourceConnection->getConnection();
$feedTable = $this->resourceConnection->getTableName('catalog_data_exporter_products');
$connection->truncateTable($feedTable);
}
}
78 changes: 66 additions & 12 deletions CatalogDataExporter/Test/Integration/AbstractProductTestHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use Magento\Catalog\Helper\Product as ProductHelper;
use Magento\Catalog\Model\Product\Attribute\Source\Status;
use Magento\Catalog\Model\Product\Visibility;
use Magento\Framework\Api\SearchCriteriaInterface;
use Magento\Framework\App\ResourceConnection;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Exception\NoSuchEntityException;
Expand Down Expand Up @@ -126,18 +127,9 @@ protected function setUp(): void
$this->taxClassSource = Bootstrap::getObjectManager()->create(TaxClassSource::class);

$this->jsonSerializer = Bootstrap::getObjectManager()->create(Json::class);
}

/**
* Run the indexer to extract product data
*
* @param array $ids
* @return void
*/
protected function runIndexer(array $ids = []) : void
{
$this->indexer->load(self::CATALOG_DATA_EXPORTER);
$this->indexer->reindexList($ids);
$this->reindexProductDataExporter();
}

/**
Expand All @@ -146,10 +138,10 @@ protected function runIndexer(array $ids = []) : void
* @param string $sku
* @param string $storeViewCode
* @return array
* @throws \Zend_Db_Statement_Exception
*/
protected function getExtractedProduct(string $sku, string $storeViewCode) : array
{
// Select data from exporter table
$query = $this->connection->select()
->from(['ex' => $this->resource->getTableName(self::CATALOG_DATA_EXPORTER)])
->where('ex.sku = ?', $sku)
Expand All @@ -163,7 +155,37 @@ protected function getExtractedProduct(string $sku, string $storeViewCode) : arr
$data[$row['sku']]['is_deleted'] = $row['is_deleted'];
$data[$row['sku']]['feedData'] = $this->jsonSerializer->unserialize($row['feed_data']);
}
return $data[$sku];

return !empty($data[$sku]) ? $data[$sku] : $data;
}

/**
* Run partial exported indexer
*
* @param array $ids
* @return void
*/
protected function emulatePartialReindexBehavior(array $ids = []) : void
{
$this->indexer->reindexList($ids);
}

/**
* Reindex all the product data exporter table for existing products
*
* @return void
*/
private function reindexProductDataExporter() : void
{
$searchCriteria = Bootstrap::getObjectManager()->create(SearchCriteriaInterface::class);

$productIds = array_map(function ($product) {
return $product->getId();
}, $this->productRepository->getList($searchCriteria)->getItems());

if (!empty($productIds)) {
$this->indexer->reindexList($productIds);
}
}

/**
Expand Down Expand Up @@ -232,6 +254,8 @@ protected function validateCategoryData(ProductInterface $product, array $extrac
*/
protected function validateBaseProductData(ProductInterface $product, array $extract, string $storeViewCode) : void
{
$this->assertNotEmpty($extract, "Exported Product Data is empty");

$storeViewId = $this->storeRepositoryInterface->get($storeViewCode)->getCode();
$storeView = $this->storeManager->getStore($storeViewId);
$websiteCode = $this->websiteRepositoryInterface->getById($storeView->getWebsiteId())->getCode();
Expand Down Expand Up @@ -498,4 +522,34 @@ protected function emulateCustomersBehaviorAfterDeleteAction(): void
// \Magento\DataExporter\Model\Query\RemovedEntitiesByModifiedAtQuery::getQuery
sleep(1);
}

/**
* @param string $sku
* @return int|null
* @throws NoSuchEntityException
*/
public function getProductId(string $sku): ?int
{
$product = $this->productRepository->get($sku);
return (int)$product->getId();
}

/**
* @return void
*/
protected function tearDown(): void
{
parent::tearDown();
$this->truncateProductDataExporterIndexTable();
}

/**
* Truncates index table
*/
private function truncateProductDataExporterIndexTable(): void
{
$connection = $this->resource->getConnection();
$feedTable = $this->resource->getTableName(self::CATALOG_DATA_EXPORTER);
$connection->truncateTable($feedTable);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,18 +78,9 @@ protected function setUp() : void
$this->categoryRepository = Bootstrap::getObjectManager()->create(CategoryRepositoryInterface::class);
$this->storeManager = Bootstrap::getObjectManager()->create(StoreManagerInterface::class);
$this->categoryFeed = Bootstrap::getObjectManager()->get(FeedPool::class)->getFeed('categories');
}

/**
* Run the indexer to extract categories data
*
* @param array $ids
* @return void
*/
protected function runIndexer(array $ids) : void
{
$this->indexer->load(self::CATEGORY_FEED_INDEXER);
$this->indexer->reindexList($ids);
$this->reindexCategoryDataExporterTable();
}

/**
Expand All @@ -101,6 +92,8 @@ protected function runIndexer(array $ids) : void
*/
protected function assertBaseCategoryData(CategoryInterface $category, array $extract, StoreInterface $store) : void
{
$this->assertNotEmpty($extract, "Exported Category Data is empty");

$storeCode = $this->storeManager->getGroup($store->getStoreGroupId())->getCode();
$websiteCode = $this->storeManager->getWebsite($store->getWebsiteId())->getCode();
$this->assertEquals($store->getCode(), $extract['storeViewCode']);
Expand Down Expand Up @@ -136,4 +129,44 @@ public function getCategoryById(int $categoryId, string $storeViewCode) : array
}
return [];
}

/**
* Run the category exporter indexer
*
* @param array $ids
* @return void
*/
protected function emulatePartialReindexBehavior(array $ids = []) : void
{
$this->indexer->reindexList($ids);
}

/**
* Reindex the full category data exporter table
*
* @return void
*/
private function reindexCategoryDataExporterTable() : void
{
$this->indexer->reindexAll();
}

/**
* @return void
*/
protected function tearDown(): void
{
parent::tearDown();
$this->truncateCategoryDataExporterIndexTable();
}

/**
* Truncates index table
*/
private function truncateCategoryDataExporterIndexTable(): void
{
$connection = $this->resource->getConnection();
$feedTable = $this->resource->getTableName(self::CATEGORY_FEED_INDEXER);
$connection->truncateTable($feedTable);
}
}
Loading

0 comments on commit 3152e42

Please sign in to comment.