From adb846e94d44a64a092b0970ddb7c3c7e9db0d39 Mon Sep 17 00:00:00 2001 From: Cid Lopes Date: Mon, 24 Jun 2024 11:58:58 +0300 Subject: [PATCH 01/17] Init category indexer --- Exception/ParentCategoryDisabledException.php | 49 +++++ Model/Category/CollectionBuilder.php | 7 - Model/Category/Repository.php | 175 ++++++++++++++++++ Model/Indexer/AbstractIndexer.php | 2 +- Model/Indexer/CategoryIndexer.php | 145 +++++++++++++++ .../ModeSwitcherConfiguration.php | 2 +- .../DimensionModeConfiguration.php | 2 +- .../ModeSwitcher.php | 5 +- .../ModeSwitcherConfiguration.php | 87 +++++++++ .../ModeSwitcherInterface.php | 2 +- Model/Indexer/ProductIndexer.php | 2 +- .../Magento/Category/Collection.php | 61 ++++++ Model/Service/Sync/AbstractBulkConsumer.php | 1 + .../Sync/Upsert/Category/BulkConsumer.php | 108 +++++++++++ .../Sync/Upsert/Category/BulkPublisher.php | 78 ++++++++ .../Service/Update/CategoryUpdateService.php | 145 +++++++++++++++ etc/di.xml | 2 +- etc/mview.xml | 9 + 18 files changed, 867 insertions(+), 15 deletions(-) create mode 100644 Exception/ParentCategoryDisabledException.php create mode 100644 Model/Category/Repository.php create mode 100644 Model/Indexer/CategoryIndexer.php rename Model/Indexer/Dimensions/{Product => Category}/ModeSwitcherConfiguration.php (98%) rename Model/Indexer/Dimensions/{Product => ModeSwitcher}/DimensionModeConfiguration.php (97%) rename Model/Indexer/Dimensions/{Product => ModeSwitcher}/ModeSwitcher.php (93%) create mode 100644 Model/Indexer/Dimensions/ModeSwitcher/ModeSwitcherConfiguration.php rename Model/Indexer/Dimensions/{ => ModeSwitcher}/ModeSwitcherInterface.php (97%) create mode 100644 Model/ResourceModel/Magento/Category/Collection.php create mode 100644 Model/Service/Sync/Upsert/Category/BulkConsumer.php create mode 100644 Model/Service/Sync/Upsert/Category/BulkPublisher.php create mode 100644 Model/Service/Update/CategoryUpdateService.php diff --git a/Exception/ParentCategoryDisabledException.php b/Exception/ParentCategoryDisabledException.php new file mode 100644 index 000000000..2a1d686fa --- /dev/null +++ b/Exception/ParentCategoryDisabledException.php @@ -0,0 +1,49 @@ + + * @copyright 2020 Nosto Solutions Ltd + * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause + * + */ + +namespace Nosto\Tagging\Exception; + +use Nosto\NostoException; +use Throwable; + +class ParentCategoryDisabledException extends NostoException +{ + public function __construct(int $categoryId, $code = 0, Throwable $previous = null) + { + $message = "Parent category is disabled for category with id: " . $categoryId; + parent::__construct($message, $code, $previous); + } +} diff --git a/Model/Category/CollectionBuilder.php b/Model/Category/CollectionBuilder.php index c408fee7b..afa10e003 100644 --- a/Model/Category/CollectionBuilder.php +++ b/Model/Category/CollectionBuilder.php @@ -45,7 +45,6 @@ use Magento\Catalog\Model\ResourceModel\Category\Collection as MagentoCategoryCollection; use Nosto\Tagging\Model\Category\Builder as NostoCategoryBuilder; use Nosto\Tagging\Model\ResourceModel\Magento\Category\CollectionBuilder as CategoryCollectionBuilder; -use Nosto\Tagging\Model\Service\Product\Category\CategoryServiceInterface; use Traversable; /** @@ -59,9 +58,6 @@ class CollectionBuilder /** @var CategoryCollectionBuilder */ private CategoryCollectionBuilder $categoryCollectionBuilder; - /** @var CategoryServiceInterface */ - private CategoryServiceInterface $categoryService; - /** @var NostoLogger */ private NostoLogger $logger; @@ -69,18 +65,15 @@ class CollectionBuilder * Collection constructor. * @param NostoCategoryBuilder $categoryBuilder * @param CategoryCollectionBuilder $categoryCollectionBuilder - * @param CategoryServiceInterface $categoryService * @param NostoLogger $logger */ public function __construct( NostoCategoryBuilder $categoryBuilder, CategoryCollectionBuilder $categoryCollectionBuilder, - CategoryServiceInterface $categoryService, NostoLogger $logger ) { $this->categoryBuilder = $categoryBuilder; $this->categoryCollectionBuilder = $categoryCollectionBuilder; - $this->categoryService = $categoryService; $this->logger = $logger; } diff --git a/Model/Category/Repository.php b/Model/Category/Repository.php new file mode 100644 index 000000000..6d9256fc3 --- /dev/null +++ b/Model/Category/Repository.php @@ -0,0 +1,175 @@ + + * @copyright 2020 Nosto Solutions Ltd + * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause + * + */ + +namespace Nosto\Tagging\Model\Category; + +use Magento\Catalog\Api\Data\CategoryInterface; +use Magento\Catalog\Api\Data\CategorySearchResultsInterface; +use Magento\Catalog\Model\CategoryRepository; +use Magento\Catalog\Model\ResourceModel\Category\Collection as CategoryCollection; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Store\Model\Store; +use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory as CategoryCollectionFactory; +use Nosto\Tagging\Exception\ParentCategoryDisabledException; + +/** + * Repository wrapper class for fetching categories + */ +class Repository +{ + private array $parentCategoryIdCache = []; + + /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ + private SearchCriteriaBuilder $searchCriteriaBuilder; + + /** @var CategoryRepository $categoryRepository */ + private CategoryRepository $categoryRepository; + + /** @var CategoryCollectionFactory $categoryCollectionFactory */ + private CategoryCollectionFactory $categoryCollectionFactory; + + /** + * @param SearchCriteriaBuilder $searchCriteriaBuilder + */ + public function __construct( + SearchCriteriaBuilder $searchCriteriaBuilder, + CategoryRepository $categoryRepository, + CategoryCollectionFactory $categoryCollectionFactory + ) { + $this->searchCriteriaBuilder = $searchCriteriaBuilder; + $this->categoryRepository = $categoryRepository; + $this->categoryCollectionFactory = $categoryCollectionFactory; + } + + /** + * Gets categories by category ids + * + * @param array $ids + */ + public function getByIds(array $ids) + { + //@TODO implement + } + + + /** + * @param Store $store + * @param array $categoryIds + * + * @return CategoryCollection + * @throws LocalizedException + */ + public function getCategoryCollectionQuery(Store $store, array $categoryIds = []) + { + $categories = $this->categoryCollectionFactory->create() + ->distinct(true) + ->addNameToResult() + ->setStoreId($store->getId()) + ->addUrlRewriteToResult() + ->addAttributeToFilter('level', ['gt' => 1]) // @TODO: Check if zero level categories are needed + ->addAttributeToSelect(array_merge(['name', 'is_active', 'include_in_menu'])) + ->addOrderField('entity_id'); + + if ($categoryIds) { + $categories->addAttributeToFilter('entity_id', ['in' => $categoryIds]); + } + + return $categories; + } + + + /** + * Gets the parent category ID's for a given category + * + * @param CategoryInterface $category + * @return string[]|null + * @throws ParentCategoryDisabledException + */ + public function resolveParentCategoryIds(CategoryInterface $category) + { + if ($this->getParentIdsFromCache($category)) { + return $this->getParentIdsFromCache($category); + } + + if ($category->getLevel() < 1 && !$category->getIsActive()) { + throw new ParentCategoryDisabledException( + sprintf( + 'Category with id %s is disabled', + $category->getId() + ) + ); + } + + $parentCategoryIds = null; + if ($category->getLevel() >= 1) { + //@TODO: get parents from root category, check if this works + $parentCategoryIds = $category->getParentIds(); + $this->saveParentIdsToCache($category, $parentCategoryIds); + } + return $parentCategoryIds; + } + + + /** + * Get parent ids from cache. Return null if the cache is not available + * + * @param CategoryInterface $category + * @return string[]|null + */ + private function getParentIdsFromCache(CategoryInterface $category) + { + if (isset($this->parentCategoryIdCache[$category->getId()])) { + return $this->parentCategoryIdCache[$category->getId()]; + } + + return null; + } + + /** + * Saves the parents category ids to internal cache to avoid redundant + * database queries + * + * @param CategoryInterface $category + * @param string[] $parentCategoryIds + */ + private function saveParentIdsToCache(CategoryInterface $category, array $parentCategoryIds) + { + $this->parentCategoryIdCache[$category->getId()] = $parentCategoryIds; + } + +} diff --git a/Model/Indexer/AbstractIndexer.php b/Model/Indexer/AbstractIndexer.php index 5ede39662..1df52cb33 100644 --- a/Model/Indexer/AbstractIndexer.php +++ b/Model/Indexer/AbstractIndexer.php @@ -51,7 +51,7 @@ use Nosto\Tagging\Helper\Scope as NostoHelperScope; use Nosto\Tagging\Logger\Logger as NostoLogger; use Nosto\Tagging\Model\Indexer\Dimensions\AbstractDimensionModeConfiguration as DimensionModeConfiguration; -use Nosto\Tagging\Model\Indexer\Dimensions\ModeSwitcherInterface; +use Nosto\Tagging\Model\Indexer\Dimensions\ModeSwitch\ModeSwitcherInterface; use Nosto\Tagging\Model\Indexer\Dimensions\StoreDimensionProvider; use Nosto\Tagging\Model\Service\Indexer\IndexerStatusServiceInterface; use Nosto\Tagging\Util\Benchmark; diff --git a/Model/Indexer/CategoryIndexer.php b/Model/Indexer/CategoryIndexer.php new file mode 100644 index 000000000..936e7a2a0 --- /dev/null +++ b/Model/Indexer/CategoryIndexer.php @@ -0,0 +1,145 @@ + + * @copyright 2020 Nosto Solutions Ltd + * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause + * + */ + +namespace Nosto\Tagging\Model\Indexer; + +use Exception; +use Magento\Indexer\Model\ProcessManager; +use Magento\Store\Model\App\Emulation; +use Magento\Store\Model\Store; +use Nosto\NostoException; +use Nosto\Tagging\Helper\Scope as NostoHelperScope; +use Nosto\Tagging\Logger\Logger as NostoLogger; +use Nosto\Tagging\Model\Indexer\Dimensions\ModeSwitch\ModeSwitcher as CategoryModeSwitcher; +use Nosto\Tagging\Model\Indexer\Dimensions\ModeSwitch\ModeSwitcherInterface; +use Nosto\Tagging\Model\Indexer\Dimensions\StoreDimensionProvider; +use Nosto\Tagging\Model\ResourceModel\Magento\Category\Collection as CategoryCollection; +use Nosto\Tagging\Model\ResourceModel\Magento\Category\CollectionBuilder; +use Nosto\Tagging\Model\Service\Indexer\IndexerStatusServiceInterface; +use Nosto\Tagging\Model\Service\Update\CategoryUpdateService; +use Symfony\Component\Console\Input\InputInterface; + +/** + * Class CategoryIndexer + * Fetches category ID's from CL tables and create entries in the message queue + */ +class CategoryIndexer extends AbstractIndexer +{ + public const INDEXER_ID = 'nosto_index_category'; + + /** @var CategoryUpdateService */ + private CategoryUpdateService $categoryUpdateService; + + /** @var CategoryModeSwitcher */ + private CategoryModeSwitcher $modeSwitcher; + + /** + * Constructor. + * @param NostoHelperScope $nostoHelperScope + * @param CategoryUpdateService $categoryUpdateService + * @param NostoLogger $logger + * @param CategoryModeSwitcher $modeSwitcher + * @param StoreDimensionProvider $dimensionProvider + * @param Emulation $storeEmulation + * @param ProcessManager $processManager + * @param InputInterface $input + * @param IndexerStatusServiceInterface $indexerStatusService + */ + public function __construct( + NostoHelperScope $nostoHelperScope, + CategoryUpdateService $categoryUpdateService, + NostoLogger $logger, + CategoryModeSwitcher $modeSwitcher, + StoreDimensionProvider $dimensionProvider, + Emulation $storeEmulation, + ProcessManager $processManager, + InputInterface $input, + IndexerStatusServiceInterface $indexerStatusService + ) { + $this->categoryUpdateService = $categoryUpdateService; + $this->modeSwitcher = $modeSwitcher; + parent::__construct( + $nostoHelperScope, + $logger, + $dimensionProvider, + $storeEmulation, + $input, + $indexerStatusService, + $processManager + ); + } + + /** + * @inheritDoc + */ + public function getModeSwitcher(): ModeSwitcherInterface + { + return $this->modeSwitcher; + } + + /** + * @inheritDoc + * @throws NostoException + * @throws Exception + */ + public function doIndex(Store $store, array $ids = []) + { + $collection = $this->getCollection($store, $ids); + $this->categoryUpdateService->addCollectionToUpdateMessageQueue( + $collection, + $store + ); + } + + /** + * @inheritDoc + */ + public function getIndexerId(): string + { + return self::INDEXER_ID; + } + + /** + * @param Store $store + * @param array $ids + * @return CategoryCollection + */ + public function getCollection(Store $store, array $ids = []) //: CategoryCollection + { + // @TODO: implement + return null; + } +} diff --git a/Model/Indexer/Dimensions/Product/ModeSwitcherConfiguration.php b/Model/Indexer/Dimensions/Category/ModeSwitcherConfiguration.php similarity index 98% rename from Model/Indexer/Dimensions/Product/ModeSwitcherConfiguration.php rename to Model/Indexer/Dimensions/Category/ModeSwitcherConfiguration.php index 8ed82a708..d29399c31 100644 --- a/Model/Indexer/Dimensions/Product/ModeSwitcherConfiguration.php +++ b/Model/Indexer/Dimensions/Category/ModeSwitcherConfiguration.php @@ -42,7 +42,7 @@ class ModeSwitcherConfiguration { - public const XML_PATH_PRODUCT_INDEX_DIMENSIONS_MODE = 'indexer/nosto_index_product/dimensions_mode'; + public const XML_PATH_PRODUCT_INDEX_DIMENSIONS_MODE = 'indexer/nosto_index_category/dimensions_mode'; /** * ConfigInterface diff --git a/Model/Indexer/Dimensions/Product/DimensionModeConfiguration.php b/Model/Indexer/Dimensions/ModeSwitcher/DimensionModeConfiguration.php similarity index 97% rename from Model/Indexer/Dimensions/Product/DimensionModeConfiguration.php rename to Model/Indexer/Dimensions/ModeSwitcher/DimensionModeConfiguration.php index 28fe424cc..d19ea03fb 100644 --- a/Model/Indexer/Dimensions/Product/DimensionModeConfiguration.php +++ b/Model/Indexer/Dimensions/ModeSwitcher/DimensionModeConfiguration.php @@ -34,7 +34,7 @@ * */ -namespace Nosto\Tagging\Model\Indexer\Dimensions\Product; +namespace Nosto\Tagging\Model\Indexer\Dimensions\ModeSwitch; use Nosto\Tagging\Model\Indexer\Dimensions\AbstractDimensionModeConfiguration; diff --git a/Model/Indexer/Dimensions/Product/ModeSwitcher.php b/Model/Indexer/Dimensions/ModeSwitcher/ModeSwitcher.php similarity index 93% rename from Model/Indexer/Dimensions/Product/ModeSwitcher.php rename to Model/Indexer/Dimensions/ModeSwitcher/ModeSwitcher.php index 122b98954..5f59e0efd 100644 --- a/Model/Indexer/Dimensions/Product/ModeSwitcher.php +++ b/Model/Indexer/Dimensions/ModeSwitcher/ModeSwitcher.php @@ -35,11 +35,12 @@ * */ -namespace Nosto\Tagging\Model\Indexer\Dimensions\Product; +namespace Nosto\Tagging\Model\Indexer\Dimensions\ModeSwitch; use Magento\Indexer\Model\DimensionMode; use Magento\Indexer\Model\DimensionModes; -use Nosto\Tagging\Model\Indexer\Dimensions\ModeSwitcherInterface; +use Nosto\Tagging\Model\Indexer\Dimensions\Product\DimensionModeConfiguration; +use Nosto\Tagging\Model\Indexer\Dimensions\Product\ModeSwitcherConfiguration; class ModeSwitcher implements ModeSwitcherInterface { diff --git a/Model/Indexer/Dimensions/ModeSwitcher/ModeSwitcherConfiguration.php b/Model/Indexer/Dimensions/ModeSwitcher/ModeSwitcherConfiguration.php new file mode 100644 index 000000000..8ce3243ff --- /dev/null +++ b/Model/Indexer/Dimensions/ModeSwitcher/ModeSwitcherConfiguration.php @@ -0,0 +1,87 @@ + + * @copyright 2020 Nosto Solutions Ltd + * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause + * + */ + +namespace Nosto\Tagging\Model\Indexer\Dimensions\ModeSwitch; + +use InvalidArgumentException; +use Magento\Framework\App\Cache\TypeListInterface; +use Magento\Framework\App\Config\ConfigResource\ConfigInterface; + +//@TODO: needs to be abstracted to include categories as well +class ModeSwitcherConfiguration +{ + public const XML_PATH_PRODUCT_INDEX_DIMENSIONS_MODE = 'indexer/nosto_index_product/dimensions_mode'; + + /** + * ConfigInterface + * + * @var ConfigInterface + */ + private ConfigInterface $configWriter; + + /** + * TypeListInterface + * + * @var TypeListInterface + */ + private TypeListInterface $cacheTypeList; + + /** + * ModeSwitcherConfiguration constructor. + * @param ConfigInterface $configWriter + * @param TypeListInterface $cacheTypeList + */ + public function __construct( + ConfigInterface $configWriter, + TypeListInterface $cacheTypeList + ) { + $this->configWriter = $configWriter; + $this->cacheTypeList = $cacheTypeList; + } + + /** + * Save switcher mode and invalidate reindex. + * + * @param string $mode + * @return void + * @throws InvalidArgumentException + */ + public function saveMode(string $mode) + { + $this->configWriter->saveConfig(self::XML_PATH_PRODUCT_INDEX_DIMENSIONS_MODE, $mode); + $this->cacheTypeList->cleanType('config'); + } +} diff --git a/Model/Indexer/Dimensions/ModeSwitcherInterface.php b/Model/Indexer/Dimensions/ModeSwitcher/ModeSwitcherInterface.php similarity index 97% rename from Model/Indexer/Dimensions/ModeSwitcherInterface.php rename to Model/Indexer/Dimensions/ModeSwitcher/ModeSwitcherInterface.php index 1b65029ed..0a2793dd6 100644 --- a/Model/Indexer/Dimensions/ModeSwitcherInterface.php +++ b/Model/Indexer/Dimensions/ModeSwitcher/ModeSwitcherInterface.php @@ -34,7 +34,7 @@ * */ -namespace Nosto\Tagging\Model\Indexer\Dimensions; +namespace Nosto\Tagging\Model\Indexer\Dimensions\ModeSwitch; use Magento\Indexer\Model\ModeSwitcherInterface as MagentoModeSwitcherInterface; diff --git a/Model/Indexer/ProductIndexer.php b/Model/Indexer/ProductIndexer.php index c6dc1e725..e45e041d4 100644 --- a/Model/Indexer/ProductIndexer.php +++ b/Model/Indexer/ProductIndexer.php @@ -43,7 +43,7 @@ use Nosto\NostoException; use Nosto\Tagging\Helper\Scope as NostoHelperScope; use Nosto\Tagging\Logger\Logger as NostoLogger; -use Nosto\Tagging\Model\Indexer\Dimensions\Product\ModeSwitcher as ProductModeSwitcher; +use Nosto\Tagging\Model\Indexer\Dimensions\ModeSwitcher as ProductModeSwitcher; use Nosto\Tagging\Model\Indexer\Dimensions\ModeSwitcherInterface; use Nosto\Tagging\Model\Indexer\Dimensions\StoreDimensionProvider; use Nosto\Tagging\Model\ResourceModel\Magento\Product\Collection as ProductCollection; diff --git a/Model/ResourceModel/Magento/Category/Collection.php b/Model/ResourceModel/Magento/Category/Collection.php new file mode 100644 index 000000000..7b654eed9 --- /dev/null +++ b/Model/ResourceModel/Magento/Category/Collection.php @@ -0,0 +1,61 @@ + + * @copyright 2020 Nosto Solutions Ltd + * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause + * + */ + +namespace Nosto\Tagging\Model\ResourceModel\Magento\Category; + +use Magento\Catalog\Model\Category\Attribute\Source\Status; +use Magento\Catalog\Model\ResourceModel\Category\Collection as MagentoCategoryCollection; + +class Collection extends MagentoCategoryCollection +{ + /** + * @return Collection + */ + public function addActiveFilter() + { + // @TODO: Here should be included in the menu + return $this->addAttributeToFilter('status', ['eq' => Status::STATUS_ENABLED]); + } + + /** + * @param array $ids + * @return Collection + */ + public function addIdsToFilter(array $ids) + { + return $this->addAttributeToFilter($this->getIdFieldName(), ['in' => $ids]); + } +} diff --git a/Model/Service/Sync/AbstractBulkConsumer.php b/Model/Service/Sync/AbstractBulkConsumer.php index 301deb93c..0f3c51abc 100644 --- a/Model/Service/Sync/AbstractBulkConsumer.php +++ b/Model/Service/Sync/AbstractBulkConsumer.php @@ -44,6 +44,7 @@ use Magento\Store\Model\App\Emulation; use Nosto\Tagging\Logger\Logger; +// @TODO: needs to be abstracted to include categories as well abstract class AbstractBulkConsumer implements BulkConsumerInterface { /** @var Logger */ diff --git a/Model/Service/Sync/Upsert/Category/BulkConsumer.php b/Model/Service/Sync/Upsert/Category/BulkConsumer.php new file mode 100644 index 000000000..2d6b268f5 --- /dev/null +++ b/Model/Service/Sync/Upsert/Category/BulkConsumer.php @@ -0,0 +1,108 @@ + + * @copyright 2020 Nosto Solutions Ltd + * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause + * + */ + +namespace Nosto\Tagging\Model\Service\Sync\Upsert; + +use Magento\Framework\EntityManager\EntityManager; +use Magento\Framework\Json\Helper\Data as JsonHelper; +use Magento\Store\Model\App\Emulation; +use Nosto\Exception\MemoryOutOfBoundsException; +use Nosto\NostoException; +use Nosto\Tagging\Helper\Scope as NostoScopeHelper; +use Nosto\Tagging\Logger\Logger; +use Nosto\Tagging\Model\ResourceModel\Magento\Product\CollectionFactory; +use Nosto\Tagging\Model\Service\Sync\AbstractBulkConsumer; + +/** + * Asynchronous Bulk Consumer + * + * Class AsyncBulkConsumer + */ +class BulkConsumer extends AbstractBulkConsumer +{ + /** @var SyncService */ + private SyncService $syncService; + + /** @var NostoScopeHelper */ + private NostoScopeHelper $nostoScopeHelper; + + /** @var CollectionFactory */ + private CollectionFactory $collectionFactory; + + /** + * AsyncBulkConsumer constructor. + * @param SyncService $syncService + * @param NostoScopeHelper $nostoScopeHelper + * @param CollectionFactory $collectionFactory + * @param JsonHelper $jsonHelper + * @param EntityManager $entityManager + * @param Emulation $storeEmulation + * @param Logger $logger + */ + public function __construct( + SyncService $syncService, + NostoScopeHelper $nostoScopeHelper, + CollectionFactory $collectionFactory, + JsonHelper $jsonHelper, + EntityManager $entityManager, + Emulation $storeEmulation, + Logger $logger + ) { + $this->syncService = $syncService; + $this->nostoScopeHelper = $nostoScopeHelper; + $this->collectionFactory = $collectionFactory; + parent::__construct( + $logger, + $jsonHelper, + $entityManager, + $storeEmulation + ); + } + + /** + * @inheritDoc + * @throws MemoryOutOfBoundsException + * @throws NostoException + */ + public function doOperation(array $categoryIds, string $storeId) + { + $store = $this->nostoScopeHelper->getStore($storeId); + $categoryCollection = $this->collectionFactory->create() + ->addIdsToFilter($categoryIds) + ->addStoreFilter($storeId); + $this->syncService->syncProducts($categoryCollection, $store); + } +} diff --git a/Model/Service/Sync/Upsert/Category/BulkPublisher.php b/Model/Service/Sync/Upsert/Category/BulkPublisher.php new file mode 100644 index 000000000..da4548519 --- /dev/null +++ b/Model/Service/Sync/Upsert/Category/BulkPublisher.php @@ -0,0 +1,78 @@ + + * @copyright 2020 Nosto Solutions Ltd + * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause + * + */ + +namespace Nosto\Tagging\Model\Service\Sync\Upsert\Category; + +use Nosto\Tagging\Model\Service\Sync\AbstractBulkPublisher; + +// @codingStandardsIgnoreFile +class BulkPublisher extends AbstractBulkPublisher +{ + public const NOSTO_SYNC_MESSAGE_QUEUE = 'nosto_category_sync.update'; + public const BULK_SIZE = 100; + + /** + * @inheritDoc + */ + public function getTopicName(): string + { + return self::NOSTO_SYNC_MESSAGE_QUEUE; + } + + /** + * @inheritDoc + */ + public function getBulkSize(): int + { + return self::BULK_SIZE; + } + + /** + * @inheritDoc + */ + public function getBulkDescription(): string + { + return sprintf('Sync %d Nosto categories', 2); + } + + /** + * @inheritDoc + */ + public function getMetaData(): string + { + return 'Sync Nosto categories'; + } +} diff --git a/Model/Service/Update/CategoryUpdateService.php b/Model/Service/Update/CategoryUpdateService.php new file mode 100644 index 000000000..7f9fa4e8a --- /dev/null +++ b/Model/Service/Update/CategoryUpdateService.php @@ -0,0 +1,145 @@ + + * @copyright 2020 Nosto Solutions Ltd + * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause + * + */ + +namespace Nosto\Tagging\Model\Service\Update; + +use Exception; +use Magento\Catalog\Api\Data\CategoryInterface; +use Magento\Store\Model\Store; +use Nosto\NostoException; +use Nosto\Tagging\Exception\ParentCategoryDisabledException; +use Nosto\Tagging\Helper\Account as NostoAccountHelper; +use Nosto\Tagging\Helper\Data as NostoDataHelper; +use Nosto\Tagging\Logger\Logger as NostoLogger; +use Nosto\Tagging\Model\Category\Repository as NostoCategoryRepository; +use Nosto\Tagging\Model\ResourceModel\Magento\Category\Collection as CategoryCollection; +use Nosto\Tagging\Model\Service\AbstractService; +use Nosto\Tagging\Util\PagingIterator; +use Nosto\Tagging\Model\Service\Sync\BulkPublisherInterface; + +class CategoryUpdateService extends AbstractService +{ + /** @var NostoCategoryRepository $nostoCategoryRepository */ + private NostoCategoryRepository $nostoCategoryRepository; + + /** @var int $batchSize */ + private int $batchSize; + + /** @var BulkPublisherInterface */ + private BulkPublisherInterface $upsertBulkPublisher; + + /** + * CategoryUpdateService constructor. + * @param NostoLogger $logger + * @param NostoDataHelper $nostoDataHelper + * @param NostoAccountHelper $nostoAccountHelper + * @param NostoCategoryRepository $nostoCategoryRepository + * @param BulkPublisherInterface $upsertBulkPublisher + * @param int $batchSize + */ + public function __construct( + NostoLogger $logger, + NostoDataHelper $nostoDataHelper, + NostoAccountHelper $nostoAccountHelper, + NostoCategoryRepository $nostoCategoryRepository, + BulkPublisherInterface $upsertBulkPublisher, + int $batchSize + ) { + parent::__construct($nostoDataHelper, $nostoAccountHelper, $logger); + $this->nostoCategoryRepository = $nostoCategoryRepository; + $this->upsertBulkPublisher = $upsertBulkPublisher; + $this->batchSize = $batchSize; + } + + /** + * Sets the categories into the message queue + * + * @param CategoryCollection $collection + * @param Store $store + * @throws NostoException + * @throws Exception + */ + public function addCollectionToUpdateMessageQueue(CategoryCollection $collection, Store $store) + { + if ($this->getAccountHelper()->findAccount($store) === null) { + $this->logDebugWithStore('No nosto account found for the store', $store); + return; + } + $collection->setPageSize($this->batchSize); + $iterator = new PagingIterator($collection); + $this->getLogger()->debugWithSource( + sprintf( + 'Adding %d categories to message queue for store %s - batch size is %s, total amount of pages %d', + $collection->getSize(), + $store->getCode(), + $this->batchSize, + $iterator->getLastPageNumber() + ), + ['storeId' => $store->getId()], + $this + ); + /** @var CategoryCollection $page */ + foreach ($iterator as $page) { + $this->upsertBulkPublisher->execute($store->getId(), $this->toParentCategoryIds($page)); + } + } + + /** + * @param CategoryCollection $collection + * @return array + */ + private function toParentCategoryIds(CategoryCollection $collection): array + { + $categoryIds = []; + /** @var CategoryInterface $category */ + foreach ($collection->getItems() as $category) { + try { + $parents = $this->nostoCategoryRepository->resolveParentCategoryIds($category); + } catch (ParentCategoryDisabledException $e) { + $this->getLogger()->debug($e->getMessage()); + continue; + } + if (!empty($parents)) { + foreach ($parents as $id) { + $categoryIds[] = $id; + } + } else { + $categoryIds[] = $category->getId(); + } + } + return array_unique($categoryIds); + } +} diff --git a/etc/di.xml b/etc/di.xml index cccf6dccd..e1db80e75 100644 --- a/etc/di.xml +++ b/etc/di.xml @@ -180,7 +180,7 @@ - Nosto\Tagging\Model\Indexer\Dimensions\Product\ModeSwitcher + Nosto\Tagging\Model\Indexer\Dimensions\ModeSwitch\ModeSwitcher diff --git a/etc/mview.xml b/etc/mview.xml index 232767d84..7f2f5d80b 100644 --- a/etc/mview.xml +++ b/etc/mview.xml @@ -64,4 +64,13 @@ + + +
+
+
+
+
+
+ From 95324ee3c0c8fe565cea6b66693a766d16b5d2e2 Mon Sep 17 00:00:00 2001 From: ugljesaspx Date: Wed, 28 Aug 2024 10:53:19 +0200 Subject: [PATCH 02/17] Configure ModeSwithcers, Refinement CategoryIndexer Class, preparation of logic for new indexer --- Model/Indexer/AbstractIndexer.php | 2 +- Model/Indexer/CategoryIndexer.php | 22 ++++++++++++++--- .../DimensionModeConfiguration.php | 2 +- .../Dimensions/ModeSwitcher/ModeSwitcher.php | 2 +- .../ModeSwitcherConfiguration.php | 2 +- .../ModeSwitcher/ModeSwitcherInterface.php | 2 +- Model/Indexer/ProductIndexer.php | 4 ++-- etc/di.xml | 24 ++++++++++++++++++- etc/indexer.xml | 5 ++++ etc/mview.xml | 14 ++++++----- 10 files changed, 62 insertions(+), 17 deletions(-) diff --git a/Model/Indexer/AbstractIndexer.php b/Model/Indexer/AbstractIndexer.php index 1df52cb33..c08107141 100644 --- a/Model/Indexer/AbstractIndexer.php +++ b/Model/Indexer/AbstractIndexer.php @@ -51,7 +51,7 @@ use Nosto\Tagging\Helper\Scope as NostoHelperScope; use Nosto\Tagging\Logger\Logger as NostoLogger; use Nosto\Tagging\Model\Indexer\Dimensions\AbstractDimensionModeConfiguration as DimensionModeConfiguration; -use Nosto\Tagging\Model\Indexer\Dimensions\ModeSwitch\ModeSwitcherInterface; +use Nosto\Tagging\Model\Indexer\Dimensions\ModeSwitcher\ModeSwitcherInterface; use Nosto\Tagging\Model\Indexer\Dimensions\StoreDimensionProvider; use Nosto\Tagging\Model\Service\Indexer\IndexerStatusServiceInterface; use Nosto\Tagging\Util\Benchmark; diff --git a/Model/Indexer/CategoryIndexer.php b/Model/Indexer/CategoryIndexer.php index 936e7a2a0..a182521a3 100644 --- a/Model/Indexer/CategoryIndexer.php +++ b/Model/Indexer/CategoryIndexer.php @@ -43,8 +43,8 @@ use Nosto\NostoException; use Nosto\Tagging\Helper\Scope as NostoHelperScope; use Nosto\Tagging\Logger\Logger as NostoLogger; -use Nosto\Tagging\Model\Indexer\Dimensions\ModeSwitch\ModeSwitcher as CategoryModeSwitcher; -use Nosto\Tagging\Model\Indexer\Dimensions\ModeSwitch\ModeSwitcherInterface; +use Nosto\Tagging\Model\Indexer\Dimensions\ModeSwitcher\ModeSwitcher as CategoryModeSwitcher; +use Nosto\Tagging\Model\Indexer\Dimensions\ModeSwitcher\ModeSwitcherInterface; use Nosto\Tagging\Model\Indexer\Dimensions\StoreDimensionProvider; use Nosto\Tagging\Model\ResourceModel\Magento\Category\Collection as CategoryCollection; use Nosto\Tagging\Model\ResourceModel\Magento\Category\CollectionBuilder; @@ -66,11 +66,15 @@ class CategoryIndexer extends AbstractIndexer /** @var CategoryModeSwitcher */ private CategoryModeSwitcher $modeSwitcher; + /** @var CollectionBuilder */ + private CollectionBuilder $categoryCollectionBuilder; + /** * Constructor. * @param NostoHelperScope $nostoHelperScope * @param CategoryUpdateService $categoryUpdateService * @param NostoLogger $logger + * @param CollectionBuilder $categoryCollectionBuilder * @param CategoryModeSwitcher $modeSwitcher * @param StoreDimensionProvider $dimensionProvider * @param Emulation $storeEmulation @@ -82,6 +86,7 @@ public function __construct( NostoHelperScope $nostoHelperScope, CategoryUpdateService $categoryUpdateService, NostoLogger $logger, + CollectionBuilder $categoryCollectionBuilder, CategoryModeSwitcher $modeSwitcher, StoreDimensionProvider $dimensionProvider, Emulation $storeEmulation, @@ -91,6 +96,8 @@ public function __construct( ) { $this->categoryUpdateService = $categoryUpdateService; $this->modeSwitcher = $modeSwitcher; + $this->categoryCollectionBuilder = $categoryCollectionBuilder; + parent::__construct( $nostoHelperScope, $logger, @@ -139,7 +146,16 @@ public function getIndexerId(): string */ public function getCollection(Store $store, array $ids = []) //: CategoryCollection { - // @TODO: implement + $this->categoryCollectionBuilder->initDefault($store); + + if (!empty($ids)) { + $this->categoryCollectionBuilder->withIds($ids); + } else { + $this->categoryCollectionBuilder->withStore($store); + } + return null; + // @TODO: Implement logic + // return $this->categoryCollectionBuilder->build(); } } diff --git a/Model/Indexer/Dimensions/ModeSwitcher/DimensionModeConfiguration.php b/Model/Indexer/Dimensions/ModeSwitcher/DimensionModeConfiguration.php index d19ea03fb..8ab3d0a4a 100644 --- a/Model/Indexer/Dimensions/ModeSwitcher/DimensionModeConfiguration.php +++ b/Model/Indexer/Dimensions/ModeSwitcher/DimensionModeConfiguration.php @@ -34,7 +34,7 @@ * */ -namespace Nosto\Tagging\Model\Indexer\Dimensions\ModeSwitch; +namespace Nosto\Tagging\Model\Indexer\Dimensions\ModeSwitcher; use Nosto\Tagging\Model\Indexer\Dimensions\AbstractDimensionModeConfiguration; diff --git a/Model/Indexer/Dimensions/ModeSwitcher/ModeSwitcher.php b/Model/Indexer/Dimensions/ModeSwitcher/ModeSwitcher.php index 5f59e0efd..3f7aec3ff 100644 --- a/Model/Indexer/Dimensions/ModeSwitcher/ModeSwitcher.php +++ b/Model/Indexer/Dimensions/ModeSwitcher/ModeSwitcher.php @@ -35,7 +35,7 @@ * */ -namespace Nosto\Tagging\Model\Indexer\Dimensions\ModeSwitch; +namespace Nosto\Tagging\Model\Indexer\Dimensions\ModeSwitcher; use Magento\Indexer\Model\DimensionMode; use Magento\Indexer\Model\DimensionModes; diff --git a/Model/Indexer/Dimensions/ModeSwitcher/ModeSwitcherConfiguration.php b/Model/Indexer/Dimensions/ModeSwitcher/ModeSwitcherConfiguration.php index 8ce3243ff..2a00e3410 100644 --- a/Model/Indexer/Dimensions/ModeSwitcher/ModeSwitcherConfiguration.php +++ b/Model/Indexer/Dimensions/ModeSwitcher/ModeSwitcherConfiguration.php @@ -34,7 +34,7 @@ * */ -namespace Nosto\Tagging\Model\Indexer\Dimensions\ModeSwitch; +namespace Nosto\Tagging\Model\Indexer\Dimensions\ModeSwitcher; use InvalidArgumentException; use Magento\Framework\App\Cache\TypeListInterface; diff --git a/Model/Indexer/Dimensions/ModeSwitcher/ModeSwitcherInterface.php b/Model/Indexer/Dimensions/ModeSwitcher/ModeSwitcherInterface.php index 0a2793dd6..6bc3f8ef5 100644 --- a/Model/Indexer/Dimensions/ModeSwitcher/ModeSwitcherInterface.php +++ b/Model/Indexer/Dimensions/ModeSwitcher/ModeSwitcherInterface.php @@ -34,7 +34,7 @@ * */ -namespace Nosto\Tagging\Model\Indexer\Dimensions\ModeSwitch; +namespace Nosto\Tagging\Model\Indexer\Dimensions\ModeSwitcher; use Magento\Indexer\Model\ModeSwitcherInterface as MagentoModeSwitcherInterface; diff --git a/Model/Indexer/ProductIndexer.php b/Model/Indexer/ProductIndexer.php index e45e041d4..1f874e017 100644 --- a/Model/Indexer/ProductIndexer.php +++ b/Model/Indexer/ProductIndexer.php @@ -43,8 +43,8 @@ use Nosto\NostoException; use Nosto\Tagging\Helper\Scope as NostoHelperScope; use Nosto\Tagging\Logger\Logger as NostoLogger; -use Nosto\Tagging\Model\Indexer\Dimensions\ModeSwitcher as ProductModeSwitcher; -use Nosto\Tagging\Model\Indexer\Dimensions\ModeSwitcherInterface; +use Nosto\Tagging\Model\Indexer\Dimensions\ModeSwitcher\ModeSwitcher as ProductModeSwitcher; +use Nosto\Tagging\Model\Indexer\Dimensions\ModeSwitcher\ModeSwitcherInterface; use Nosto\Tagging\Model\Indexer\Dimensions\StoreDimensionProvider; use Nosto\Tagging\Model\ResourceModel\Magento\Product\Collection as ProductCollection; use Nosto\Tagging\Model\ResourceModel\Magento\Product\CollectionBuilder; diff --git a/etc/di.xml b/etc/di.xml index e1db80e75..ae083f943 100644 --- a/etc/di.xml +++ b/etc/di.xml @@ -180,8 +180,19 @@ - Nosto\Tagging\Model\Indexer\Dimensions\ModeSwitch\ModeSwitcher + Nosto\Tagging\Model\Indexer\Dimensions\ModeSwitcher\ModeSwitcher + + Nosto\Tagging\Model\Indexer\Dimensions\ModeSwitcher\ModeSwitcher + + + + + + + + + Nosto\Tagging\Model\Indexer\Dimensions\StoreDimensionProvider @@ -238,4 +249,15 @@ 500 + + + + Nosto\Tagging\Model\Service\Sync\Upsert\AsyncBulkPublisher + + + Nosto\Tagging\Model\Service\Sync\Delete\AsyncBulkPublisher + + 500 + + \ No newline at end of file diff --git a/etc/indexer.xml b/etc/indexer.xml index 00183f6ee..d6a3b0508 100644 --- a/etc/indexer.xml +++ b/etc/indexer.xml @@ -38,4 +38,9 @@ Nosto Product Indexer Populates message queue with product ids to be sent to Nosto + + Nosto Category Indexer + + Category + diff --git a/etc/mview.xml b/etc/mview.xml index 7f2f5d80b..2ffe539e3 100644 --- a/etc/mview.xml +++ b/etc/mview.xml @@ -66,11 +66,13 @@ -
-
-
-
-
-
+ +
+
+
+
+
+
+ From ced42705b172ea21a8890b83be44246e3c87b393 Mon Sep 17 00:00:00 2001 From: ugljesaspx Date: Tue, 3 Sep 2024 14:18:56 +0200 Subject: [PATCH 03/17] Category indexer --- Exception/ParentCategoryDisabledException.php | 2 +- Model/Indexer/CategoryIndexer.php | 7 +++---- Model/ResourceModel/Magento/Category/Collection.php | 10 ++++++++-- .../Magento/Category/CollectionBuilder.php | 12 ++++++------ .../{BulkConsumer.php => AsyncBulkConsumer.php} | 8 ++++++-- Model/Service/Sync/Upsert/Category/BulkPublisher.php | 3 +++ etc/communication.xml | 3 +++ etc/di.xml | 11 +++++++++-- etc/queue.xml | 6 ++++++ etc/queue_consumer.xml | 6 ++++++ etc/queue_publisher.xml | 3 +++ 11 files changed, 54 insertions(+), 17 deletions(-) rename Model/Service/Sync/Upsert/Category/{BulkConsumer.php => AsyncBulkConsumer.php} (94%) diff --git a/Exception/ParentCategoryDisabledException.php b/Exception/ParentCategoryDisabledException.php index 2a1d686fa..2192070e6 100644 --- a/Exception/ParentCategoryDisabledException.php +++ b/Exception/ParentCategoryDisabledException.php @@ -41,7 +41,7 @@ class ParentCategoryDisabledException extends NostoException { - public function __construct(int $categoryId, $code = 0, Throwable $previous = null) + public function __construct($categoryId, $code = 0, Throwable $previous = null) { $message = "Parent category is disabled for category with id: " . $categoryId; parent::__construct($message, $code, $previous); diff --git a/Model/Indexer/CategoryIndexer.php b/Model/Indexer/CategoryIndexer.php index a182521a3..2cefa7069 100644 --- a/Model/Indexer/CategoryIndexer.php +++ b/Model/Indexer/CategoryIndexer.php @@ -125,6 +125,7 @@ public function getModeSwitcher(): ModeSwitcherInterface public function doIndex(Store $store, array $ids = []) { $collection = $this->getCollection($store, $ids); + $this->categoryUpdateService->addCollectionToUpdateMessageQueue( $collection, $store @@ -144,7 +145,7 @@ public function getIndexerId(): string * @param array $ids * @return CategoryCollection */ - public function getCollection(Store $store, array $ids = []) //: CategoryCollection + public function getCollection(Store $store, array $ids = []) : CategoryCollection { $this->categoryCollectionBuilder->initDefault($store); @@ -154,8 +155,6 @@ public function getCollection(Store $store, array $ids = []) //: CategoryCollect $this->categoryCollectionBuilder->withStore($store); } - return null; - // @TODO: Implement logic - // return $this->categoryCollectionBuilder->build(); + return $this->categoryCollectionBuilder->build(); } } diff --git a/Model/ResourceModel/Magento/Category/Collection.php b/Model/ResourceModel/Magento/Category/Collection.php index 7b654eed9..74d4791e9 100644 --- a/Model/ResourceModel/Magento/Category/Collection.php +++ b/Model/ResourceModel/Magento/Category/Collection.php @@ -36,7 +36,7 @@ namespace Nosto\Tagging\Model\ResourceModel\Magento\Category; -use Magento\Catalog\Model\Category\Attribute\Source\Status; +//use Magento\Catalog\Model\Category\Attribute\Source\Status; use Magento\Catalog\Model\ResourceModel\Category\Collection as MagentoCategoryCollection; class Collection extends MagentoCategoryCollection @@ -46,8 +46,11 @@ class Collection extends MagentoCategoryCollection */ public function addActiveFilter() { + echo '
';
+        print_r(44);
+        die;
         // @TODO: Here should be included in the menu
-        return $this->addAttributeToFilter('status', ['eq' => Status::STATUS_ENABLED]);
+        return $this->addAttributeToFilter('status', ['eq' => 1]);
     }
 
     /**
@@ -56,6 +59,9 @@ public function addActiveFilter()
      */
     public function addIdsToFilter(array $ids)
     {
+        echo '
';
+        print_r(222);
+        die;
         return $this->addAttributeToFilter($this->getIdFieldName(), ['in' => $ids]);
     }
 }
diff --git a/Model/ResourceModel/Magento/Category/CollectionBuilder.php b/Model/ResourceModel/Magento/Category/CollectionBuilder.php
index 90d8ab28a..62d4637d6 100644
--- a/Model/ResourceModel/Magento/Category/CollectionBuilder.php
+++ b/Model/ResourceModel/Magento/Category/CollectionBuilder.php
@@ -39,7 +39,8 @@
 use Magento\Framework\Exception\LocalizedException;
 use Magento\Sales\Api\Data\EntityInterface;
 use Magento\Store\Model\Store;
-use Magento\Catalog\Model\ResourceModel\Category\Collection as CategoryCollection;
+//use Magento\Catalog\Model\ResourceModel\Category\Collection as CategoryCollection;
+use Nosto\Tagging\Model\ResourceModel\Magento\Category\Collection  as CategoryCollection;
 use Magento\Framework\DB\Select;
 
 /**
@@ -47,16 +48,15 @@
  */
 class CollectionBuilder
 {
+
     /** @var CategoryCollection */
     private CategoryCollection $categoryCollection;
 
     /**
-     * Collection constructor.
-     * @param CategoryCollection $categoryCollection,
+     * @param CategoryCollection $categoryCollection
      */
-    public function __construct(
-        CategoryCollection $categoryCollection
-    ) {
+    public function __construct(CategoryCollection $categoryCollection)
+    {
         $this->categoryCollection = $categoryCollection;
     }
 
diff --git a/Model/Service/Sync/Upsert/Category/BulkConsumer.php b/Model/Service/Sync/Upsert/Category/AsyncBulkConsumer.php
similarity index 94%
rename from Model/Service/Sync/Upsert/Category/BulkConsumer.php
rename to Model/Service/Sync/Upsert/Category/AsyncBulkConsumer.php
index 2d6b268f5..9f4558c30 100644
--- a/Model/Service/Sync/Upsert/Category/BulkConsumer.php
+++ b/Model/Service/Sync/Upsert/Category/AsyncBulkConsumer.php
@@ -34,7 +34,7 @@
  *
  */
 
-namespace Nosto\Tagging\Model\Service\Sync\Upsert;
+namespace Nosto\Tagging\Model\Service\Sync\Upsert\Category;
 
 use Magento\Framework\EntityManager\EntityManager;
 use Magento\Framework\Json\Helper\Data as JsonHelper;
@@ -45,13 +45,14 @@
 use Nosto\Tagging\Logger\Logger;
 use Nosto\Tagging\Model\ResourceModel\Magento\Product\CollectionFactory;
 use Nosto\Tagging\Model\Service\Sync\AbstractBulkConsumer;
+use Nosto\Tagging\Model\Service\Sync\Upsert\SyncService;
 
 /**
  * Asynchronous Bulk Consumer
  *
  * Class AsyncBulkConsumer
  */
-class BulkConsumer extends AbstractBulkConsumer
+class AsyncBulkConsumer extends AbstractBulkConsumer
 {
     /** @var SyncService */
     private SyncService $syncService;
@@ -99,6 +100,9 @@ public function __construct(
      */
     public function doOperation(array $categoryIds, string $storeId)
     {
+        echo '
';
+        print_r(122);
+        die;
         $store = $this->nostoScopeHelper->getStore($storeId);
         $categoryCollection = $this->collectionFactory->create()
             ->addIdsToFilter($categoryIds)
diff --git a/Model/Service/Sync/Upsert/Category/BulkPublisher.php b/Model/Service/Sync/Upsert/Category/BulkPublisher.php
index da4548519..8f67fd206 100644
--- a/Model/Service/Sync/Upsert/Category/BulkPublisher.php
+++ b/Model/Service/Sync/Upsert/Category/BulkPublisher.php
@@ -49,6 +49,9 @@ class BulkPublisher extends AbstractBulkPublisher
      */
     public function getTopicName(): string
     {
+        echo '
';
+        print_r(122);
+        die;
         return self::NOSTO_SYNC_MESSAGE_QUEUE;
     }
 
diff --git a/etc/communication.xml b/etc/communication.xml
index bbbd74187..0c5e284c4 100644
--- a/etc/communication.xml
+++ b/etc/communication.xml
@@ -41,4 +41,7 @@
     
         
     
+    
+        
+    
 
diff --git a/etc/di.xml b/etc/di.xml
index ae083f943..622e4ec52 100644
--- a/etc/di.xml
+++ b/etc/di.xml
@@ -224,6 +224,13 @@
             
         
     
+    
+        
+            
+                Nosto\Tagging\Model\Service\Sync\Upsert\Category\AsyncBulkConsumer
+            
+        
+    
     
         
             
@@ -251,8 +258,8 @@
     
     
         
-            
-                Nosto\Tagging\Model\Service\Sync\Upsert\AsyncBulkPublisher
+            
+                Nosto\Tagging\Model\Service\Sync\Upsert\Category\BulkPublisher
             
             
                 Nosto\Tagging\Model\Service\Sync\Delete\AsyncBulkPublisher
diff --git a/etc/queue.xml b/etc/queue.xml
index f8ad78b09..8c2167e08 100644
--- a/etc/queue.xml
+++ b/etc/queue.xml
@@ -47,4 +47,10 @@
                consumerInstance="Magento\Framework\MessageQueue\Consumer"
                handler="Nosto\Tagging\Model\Service\Sync\Delete\AsyncBulkConsumer::process"/>
     
+    
+        
+    
 
diff --git a/etc/queue_consumer.xml b/etc/queue_consumer.xml
index ea59ccd0b..fb36407aa 100644
--- a/etc/queue_consumer.xml
+++ b/etc/queue_consumer.xml
@@ -47,4 +47,10 @@
               maxMessages="100"
               consumerInstance="Magento\Framework\MessageQueue\Consumer"
               handler="Nosto\Tagging\Model\Service\Sync\Delete\AsyncBulkConsumer::processOperation"/>
+    
 
diff --git a/etc/queue_publisher.xml b/etc/queue_publisher.xml
index 1194a0372..e27b67b41 100644
--- a/etc/queue_publisher.xml
+++ b/etc/queue_publisher.xml
@@ -41,4 +41,7 @@
     
         
     
+    
+        
+    
 

From 34b89615e678ffc821722d2f458de73727ab09ea Mon Sep 17 00:00:00 2001
From: ugljesaspx 
Date: Mon, 16 Sep 2024 20:13:23 +0200
Subject: [PATCH 04/17] Fixing unused classes, and change names

---
 .../Dimensions/Category/ModeSwitcherConfiguration.php      | 2 +-
 Model/Indexer/Dimensions/ModeSwitcher/ModeSwitcher.php     | 4 ++--
 Model/ResourceModel/Magento/Category/Collection.php        | 7 +------
 Model/Service/Sync/Upsert/Category/AsyncBulkConsumer.php   | 5 +----
 Model/Service/Sync/Upsert/Category/BulkPublisher.php       | 3 ---
 5 files changed, 5 insertions(+), 16 deletions(-)

diff --git a/Model/Indexer/Dimensions/Category/ModeSwitcherConfiguration.php b/Model/Indexer/Dimensions/Category/ModeSwitcherConfiguration.php
index d29399c31..a44acc69c 100644
--- a/Model/Indexer/Dimensions/Category/ModeSwitcherConfiguration.php
+++ b/Model/Indexer/Dimensions/Category/ModeSwitcherConfiguration.php
@@ -34,7 +34,7 @@
  *
  */
 
-namespace Nosto\Tagging\Model\Indexer\Dimensions\Product;
+namespace Nosto\Tagging\Model\Indexer\Dimensions\Category;
 
 use InvalidArgumentException;
 use Magento\Framework\App\Cache\TypeListInterface;
diff --git a/Model/Indexer/Dimensions/ModeSwitcher/ModeSwitcher.php b/Model/Indexer/Dimensions/ModeSwitcher/ModeSwitcher.php
index 3f7aec3ff..395899977 100644
--- a/Model/Indexer/Dimensions/ModeSwitcher/ModeSwitcher.php
+++ b/Model/Indexer/Dimensions/ModeSwitcher/ModeSwitcher.php
@@ -39,8 +39,8 @@
 
 use Magento\Indexer\Model\DimensionMode;
 use Magento\Indexer\Model\DimensionModes;
-use Nosto\Tagging\Model\Indexer\Dimensions\Product\DimensionModeConfiguration;
-use Nosto\Tagging\Model\Indexer\Dimensions\Product\ModeSwitcherConfiguration;
+use Nosto\Tagging\Model\Indexer\Dimensions\ModeSwitcher\DimensionModeConfiguration;
+use Nosto\Tagging\Model\Indexer\Dimensions\ModeSwitcher\ModeSwitcherConfiguration;
 
 class ModeSwitcher implements ModeSwitcherInterface
 {
diff --git a/Model/ResourceModel/Magento/Category/Collection.php b/Model/ResourceModel/Magento/Category/Collection.php
index 74d4791e9..d3b3dac93 100644
--- a/Model/ResourceModel/Magento/Category/Collection.php
+++ b/Model/ResourceModel/Magento/Category/Collection.php
@@ -46,9 +46,6 @@ class Collection extends MagentoCategoryCollection
      */
     public function addActiveFilter()
     {
-        echo '
';
-        print_r(44);
-        die;
         // @TODO: Here should be included in the menu
         return $this->addAttributeToFilter('status', ['eq' => 1]);
     }
@@ -59,9 +56,7 @@ public function addActiveFilter()
      */
     public function addIdsToFilter(array $ids)
     {
-        echo '
';
-        print_r(222);
-        die;
+
         return $this->addAttributeToFilter($this->getIdFieldName(), ['in' => $ids]);
     }
 }
diff --git a/Model/Service/Sync/Upsert/Category/AsyncBulkConsumer.php b/Model/Service/Sync/Upsert/Category/AsyncBulkConsumer.php
index 9f4558c30..cccef0fa4 100644
--- a/Model/Service/Sync/Upsert/Category/AsyncBulkConsumer.php
+++ b/Model/Service/Sync/Upsert/Category/AsyncBulkConsumer.php
@@ -43,7 +43,7 @@
 use Nosto\NostoException;
 use Nosto\Tagging\Helper\Scope as NostoScopeHelper;
 use Nosto\Tagging\Logger\Logger;
-use Nosto\Tagging\Model\ResourceModel\Magento\Product\CollectionFactory;
+use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory;
 use Nosto\Tagging\Model\Service\Sync\AbstractBulkConsumer;
 use Nosto\Tagging\Model\Service\Sync\Upsert\SyncService;
 
@@ -100,9 +100,6 @@ public function __construct(
      */
     public function doOperation(array $categoryIds, string $storeId)
     {
-        echo '
';
-        print_r(122);
-        die;
         $store = $this->nostoScopeHelper->getStore($storeId);
         $categoryCollection = $this->collectionFactory->create()
             ->addIdsToFilter($categoryIds)
diff --git a/Model/Service/Sync/Upsert/Category/BulkPublisher.php b/Model/Service/Sync/Upsert/Category/BulkPublisher.php
index 8f67fd206..da4548519 100644
--- a/Model/Service/Sync/Upsert/Category/BulkPublisher.php
+++ b/Model/Service/Sync/Upsert/Category/BulkPublisher.php
@@ -49,9 +49,6 @@ class BulkPublisher extends AbstractBulkPublisher
      */
     public function getTopicName(): string
     {
-        echo '
';
-        print_r(122);
-        die;
         return self::NOSTO_SYNC_MESSAGE_QUEUE;
     }
 

From 3ce60f6a89aa703a03f3cc3c3e9edbe04df675ec Mon Sep 17 00:00:00 2001
From: ugljesaspx 
Date: Tue, 17 Sep 2024 15:54:48 +0200
Subject: [PATCH 05/17] Fix warning errors

---
 Model/Category/Repository.php | 2 --
 1 file changed, 2 deletions(-)

diff --git a/Model/Category/Repository.php b/Model/Category/Repository.php
index 6d9256fc3..ec22a9b3e 100644
--- a/Model/Category/Repository.php
+++ b/Model/Category/Repository.php
@@ -37,12 +37,10 @@
 namespace Nosto\Tagging\Model\Category;
 
 use Magento\Catalog\Api\Data\CategoryInterface;
-use Magento\Catalog\Api\Data\CategorySearchResultsInterface;
 use Magento\Catalog\Model\CategoryRepository;
 use Magento\Catalog\Model\ResourceModel\Category\Collection as CategoryCollection;
 use Magento\Framework\Api\SearchCriteriaBuilder;
 use Magento\Framework\Exception\LocalizedException;
-use Magento\Framework\Exception\NoSuchEntityException;
 use Magento\Store\Model\Store;
 use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory as CategoryCollectionFactory;
 use Nosto\Tagging\Exception\ParentCategoryDisabledException;

From 3de71b32bb2fabe30fa7728210f5b52e85e95b0d Mon Sep 17 00:00:00 2001
From: ugljesaspx 
Date: Tue, 17 Sep 2024 16:23:12 +0200
Subject: [PATCH 06/17] Fix warnings and errors

---
 Model/Category/Repository.php                       |  9 +++------
 Model/ResourceModel/Magento/Category/Collection.php | 10 +++++-----
 Model/Service/Update/CategoryUpdateService.php      |  3 ++-
 3 files changed, 10 insertions(+), 12 deletions(-)

diff --git a/Model/Category/Repository.php b/Model/Category/Repository.php
index ec22a9b3e..21cc14309 100644
--- a/Model/Category/Repository.php
+++ b/Model/Category/Repository.php
@@ -37,6 +37,7 @@
 namespace Nosto\Tagging\Model\Category;
 
 use Magento\Catalog\Api\Data\CategoryInterface;
+use Magento\Catalog\Model\Category;
 use Magento\Catalog\Model\CategoryRepository;
 use Magento\Catalog\Model\ResourceModel\Category\Collection as CategoryCollection;
 use Magento\Framework\Api\SearchCriteriaBuilder;
@@ -84,7 +85,6 @@ public function getByIds(array $ids)
         //@TODO implement
     }
 
-
     /**
      * @param Store $store
      * @param array $categoryIds
@@ -110,15 +110,13 @@ public function getCategoryCollectionQuery(Store $store, array $categoryIds = []
         return $categories;
     }
 
-
     /**
      * Gets the parent category ID's for a given category
      *
-     * @param CategoryInterface $category
      * @return string[]|null
      * @throws ParentCategoryDisabledException
      */
-    public function resolveParentCategoryIds(CategoryInterface $category)
+    public function resolveParentCategoryIds(Category $category): ?array
     {
         if ($this->getParentIdsFromCache($category)) {
             return $this->getParentIdsFromCache($category);
@@ -135,14 +133,13 @@ public function resolveParentCategoryIds(CategoryInterface $category)
 
         $parentCategoryIds = null;
         if ($category->getLevel() >= 1) {
-            //@TODO: get parents from root category, check if this works
             $parentCategoryIds = $category->getParentIds();
             $this->saveParentIdsToCache($category, $parentCategoryIds);
         }
+
         return $parentCategoryIds;
     }
 
-
     /**
      * Get parent ids from cache. Return null if the cache is not available
      *
diff --git a/Model/ResourceModel/Magento/Category/Collection.php b/Model/ResourceModel/Magento/Category/Collection.php
index d3b3dac93..44703b881 100644
--- a/Model/ResourceModel/Magento/Category/Collection.php
+++ b/Model/ResourceModel/Magento/Category/Collection.php
@@ -36,27 +36,27 @@
 
 namespace Nosto\Tagging\Model\ResourceModel\Magento\Category;
 
-//use Magento\Catalog\Model\Category\Attribute\Source\Status;
 use Magento\Catalog\Model\ResourceModel\Category\Collection as MagentoCategoryCollection;
+use Magento\Framework\Exception\LocalizedException;
 
 class Collection extends MagentoCategoryCollection
 {
     /**
      * @return Collection
+     * @throws LocalizedException
      */
-    public function addActiveFilter()
+    public function addActiveFilter(): Collection
     {
-        // @TODO: Here should be included in the menu
         return $this->addAttributeToFilter('status', ['eq' => 1]);
     }
 
     /**
      * @param array $ids
      * @return Collection
+     * @throws LocalizedException
      */
-    public function addIdsToFilter(array $ids)
+    public function addIdsToFilter(array $ids): Collection
     {
-
         return $this->addAttributeToFilter($this->getIdFieldName(), ['in' => $ids]);
     }
 }
diff --git a/Model/Service/Update/CategoryUpdateService.php b/Model/Service/Update/CategoryUpdateService.php
index 7f9fa4e8a..aeac25f20 100644
--- a/Model/Service/Update/CategoryUpdateService.php
+++ b/Model/Service/Update/CategoryUpdateService.php
@@ -38,6 +38,7 @@
 
 use Exception;
 use Magento\Catalog\Api\Data\CategoryInterface;
+use Magento\Catalog\Model\Category;
 use Magento\Store\Model\Store;
 use Nosto\NostoException;
 use Nosto\Tagging\Exception\ParentCategoryDisabledException;
@@ -124,7 +125,7 @@ public function addCollectionToUpdateMessageQueue(CategoryCollection $collection
     private function toParentCategoryIds(CategoryCollection $collection): array
     {
         $categoryIds = [];
-        /** @var CategoryInterface $category */
+        /** @var Category $category */
         foreach ($collection->getItems() as $category) {
             try {
                 $parents = $this->nostoCategoryRepository->resolveParentCategoryIds($category);

From a65c321fd99cab3ecc830b83b261c67b9cce6afe Mon Sep 17 00:00:00 2001
From: ugljesaspx 
Date: Wed, 18 Sep 2024 11:40:40 +0200
Subject: [PATCH 07/17] Fixing unused classes, and change names

---
 Model/Category/Repository.php                  | 2 +-
 Model/Service/Update/CategoryUpdateService.php | 3 ++-
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/Model/Category/Repository.php b/Model/Category/Repository.php
index 21cc14309..ab6d9c073 100644
--- a/Model/Category/Repository.php
+++ b/Model/Category/Repository.php
@@ -116,7 +116,7 @@ public function getCategoryCollectionQuery(Store $store, array $categoryIds = []
      * @return string[]|null
      * @throws ParentCategoryDisabledException
      */
-    public function resolveParentCategoryIds(Category $category): ?array
+    public function resolveParentCategoryIds(CategoryInterface $category): ?array
     {
         if ($this->getParentIdsFromCache($category)) {
             return $this->getParentIdsFromCache($category);
diff --git a/Model/Service/Update/CategoryUpdateService.php b/Model/Service/Update/CategoryUpdateService.php
index aeac25f20..d805efa2d 100644
--- a/Model/Service/Update/CategoryUpdateService.php
+++ b/Model/Service/Update/CategoryUpdateService.php
@@ -125,7 +125,7 @@ public function addCollectionToUpdateMessageQueue(CategoryCollection $collection
     private function toParentCategoryIds(CategoryCollection $collection): array
     {
         $categoryIds = [];
-        /** @var Category $category */
+        /** @var CategoryInterface $category */
         foreach ($collection->getItems() as $category) {
             try {
                 $parents = $this->nostoCategoryRepository->resolveParentCategoryIds($category);
@@ -141,6 +141,7 @@ private function toParentCategoryIds(CategoryCollection $collection): array
                 $categoryIds[] = $category->getId();
             }
         }
+
         return array_unique($categoryIds);
     }
 }

From e7bafbf3ede606259c6d0ba56113263cded99816 Mon Sep 17 00:00:00 2001
From: ugljesaspx 
Date: Wed, 18 Sep 2024 16:05:28 +0200
Subject: [PATCH 08/17] Fixing unused classes, and change names, test parent
 category ids fnc

---
 Model/Category/Repository.php | 18 +++++++++++++++++-
 1 file changed, 17 insertions(+), 1 deletion(-)

diff --git a/Model/Category/Repository.php b/Model/Category/Repository.php
index ab6d9c073..607fee226 100644
--- a/Model/Category/Repository.php
+++ b/Model/Category/Repository.php
@@ -133,13 +133,29 @@ public function resolveParentCategoryIds(CategoryInterface $category): ?array
 
         $parentCategoryIds = null;
         if ($category->getLevel() >= 1) {
-            $parentCategoryIds = $category->getParentIds();
+            $parentCategoryIds = $this->getCategoryParentIds($category['path']);
+
             $this->saveParentIdsToCache($category, $parentCategoryIds);
         }
 
         return $parentCategoryIds;
     }
 
+    /**
+     * Get Parent Category IDS
+     *
+     * @param $path
+     * @return array
+     */
+    private function getCategoryParentIds($path): array
+    {
+        $parentCategories = explode('/', $path);
+
+        array_pop($parentCategories);
+
+        return $parentCategories;
+    }
+
     /**
      * Get parent ids from cache. Return null if the cache is not available
      *

From 8537de3479b2663b7e6c15121c6b56b67a03dcbf Mon Sep 17 00:00:00 2001
From: Cid Lopes 
Date: Mon, 30 Sep 2024 14:27:03 +0300
Subject: [PATCH 09/17] Update Model/Category/Repository.php

---
 Model/Category/Repository.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Model/Category/Repository.php b/Model/Category/Repository.php
index 607fee226..e44768a25 100644
--- a/Model/Category/Repository.php
+++ b/Model/Category/Repository.php
@@ -99,7 +99,7 @@ public function getCategoryCollectionQuery(Store $store, array $categoryIds = []
             ->addNameToResult()
             ->setStoreId($store->getId())
             ->addUrlRewriteToResult()
-            ->addAttributeToFilter('level', ['gt' => 1]) // @TODO: Check if zero level categories are needed
+            ->addAttributeToFilter('level', ['gt' => 1])
             ->addAttributeToSelect(array_merge(['name', 'is_active', 'include_in_menu']))
             ->addOrderField('entity_id');
 

From 22444cf87034122dcd9bd9115ccb61fbbe3954d7 Mon Sep 17 00:00:00 2001
From: Cid Lopes 
Date: Mon, 30 Sep 2024 14:27:08 +0300
Subject: [PATCH 10/17] Update Model/Category/Repository.php

---
 Model/Category/Repository.php | 1 -
 1 file changed, 1 deletion(-)

diff --git a/Model/Category/Repository.php b/Model/Category/Repository.php
index e44768a25..156a292c0 100644
--- a/Model/Category/Repository.php
+++ b/Model/Category/Repository.php
@@ -37,7 +37,6 @@
 namespace Nosto\Tagging\Model\Category;
 
 use Magento\Catalog\Api\Data\CategoryInterface;
-use Magento\Catalog\Model\Category;
 use Magento\Catalog\Model\CategoryRepository;
 use Magento\Catalog\Model\ResourceModel\Category\Collection as CategoryCollection;
 use Magento\Framework\Api\SearchCriteriaBuilder;

From dac61dc9b794ae3e87e483e9035f8ccf3a650ffc Mon Sep 17 00:00:00 2001
From: Cid Lopes 
Date: Mon, 30 Sep 2024 14:27:13 +0300
Subject: [PATCH 11/17] Update
 Model/Indexer/Dimensions/ModeSwitcher/ModeSwitcherConfiguration.php

---
 .../Dimensions/ModeSwitcher/ModeSwitcherConfiguration.php        | 1 -
 1 file changed, 1 deletion(-)

diff --git a/Model/Indexer/Dimensions/ModeSwitcher/ModeSwitcherConfiguration.php b/Model/Indexer/Dimensions/ModeSwitcher/ModeSwitcherConfiguration.php
index 2a00e3410..0f1cecee1 100644
--- a/Model/Indexer/Dimensions/ModeSwitcher/ModeSwitcherConfiguration.php
+++ b/Model/Indexer/Dimensions/ModeSwitcher/ModeSwitcherConfiguration.php
@@ -40,7 +40,6 @@
 use Magento\Framework\App\Cache\TypeListInterface;
 use Magento\Framework\App\Config\ConfigResource\ConfigInterface;
 
-//@TODO: needs to be abstracted to include categories as well
 class ModeSwitcherConfiguration
 {
     public const XML_PATH_PRODUCT_INDEX_DIMENSIONS_MODE = 'indexer/nosto_index_product/dimensions_mode';

From 7d9b919725eaed053720a6c1da53f7ce1f037658 Mon Sep 17 00:00:00 2001
From: Cid Lopes 
Date: Mon, 30 Sep 2024 14:27:21 +0300
Subject: [PATCH 12/17] Update Model/Service/Update/CategoryUpdateService.php

---
 Model/Service/Update/CategoryUpdateService.php | 1 -
 1 file changed, 1 deletion(-)

diff --git a/Model/Service/Update/CategoryUpdateService.php b/Model/Service/Update/CategoryUpdateService.php
index d805efa2d..8691dd594 100644
--- a/Model/Service/Update/CategoryUpdateService.php
+++ b/Model/Service/Update/CategoryUpdateService.php
@@ -38,7 +38,6 @@
 
 use Exception;
 use Magento\Catalog\Api\Data\CategoryInterface;
-use Magento\Catalog\Model\Category;
 use Magento\Store\Model\Store;
 use Nosto\NostoException;
 use Nosto\Tagging\Exception\ParentCategoryDisabledException;

From d06c586b5b871fddd8de0cd6738191f9b3129f7d Mon Sep 17 00:00:00 2001
From: ugljesaspx 
Date: Fri, 1 Nov 2024 14:06:11 +0100
Subject: [PATCH 13/17] Category connection with Queue

---
 Model/Service/Sync/AbstractBulkPublisher.php  |  15 +--
 Model/Service/Sync/BulkPublisherInterface.php |   4 +-
 .../Upsert/Category/AsyncBulkConsumer.php     |  10 +-
 ...lkPublisher.php => AsyncBulkPublisher.php} |   2 +-
 Plugin/CategoryUpdate.php                     | 121 ++++++++++++++++++
 etc/di.xml                                    |  16 ++-
 etc/queue_consumer.xml                        |   2 +-
 etc/queue_topology.xml                        |   1 +
 8 files changed, 148 insertions(+), 23 deletions(-)
 rename Model/Service/Sync/Upsert/Category/{BulkPublisher.php => AsyncBulkPublisher.php} (97%)
 create mode 100644 Plugin/CategoryUpdate.php

diff --git a/Model/Service/Sync/AbstractBulkPublisher.php b/Model/Service/Sync/AbstractBulkPublisher.php
index 2df16c360..efbf6e81c 100644
--- a/Model/Service/Sync/AbstractBulkPublisher.php
+++ b/Model/Service/Sync/AbstractBulkPublisher.php
@@ -100,37 +100,36 @@ public function __construct(// @codingStandardsIgnoreLine
      * @inheritDoc
      * @throws LocalizedException
      */
-    public function execute(int $storeId, array $productIds = [])
+    public function execute(int $storeId, array $entityIds = [])
     {
-        if (!empty($productIds)) {
-            $this->publishCollectionToMessageQueue($storeId, $productIds);
+        if (!empty($entityIds)) {
+            $this->publishCollectionToMessageQueue($storeId, $entityIds);
         }
     }
 
     /**
      * @param $storeId
-     * @param $productIds
+     * @param $entityIds
      * @throws LocalizedException
      * @throws Exception
      */
     private function publishCollectionToMessageQueue(
         $storeId,
-        $productIds
+        $entityIds
     ) {
-
         if (!$this->canUseAsyncOperations()) {
             $this->logger->critical(
                 "Module Magento_AsynchronousOperations not available. Aborting bulk publish operation"
             );
             return;
         }
-        $productIdsChunks = array_chunk($productIds, $this->getBulkSize());
+        $productIdsChunks = array_chunk($entityIds, $this->getBulkSize());
         $bulkUuid = $this->identityService->generateId();
         /**
          * Argument is of type string but array is expected
          */
         /** @phan-suppress-next-line PhanTypeMismatchArgumentProbablyReal */
-        $bulkDescription = __('Sync ' . count($productIds) . ' Nosto products');
+        $bulkDescription = __('Sync ' . count($entityIds) . ' Nosto products');
         $operationsData = [];
         foreach ($productIdsChunks as $productIdsChunk) {
             $operationsData[] = $this->buildOperationData(
diff --git a/Model/Service/Sync/BulkPublisherInterface.php b/Model/Service/Sync/BulkPublisherInterface.php
index b4e3107d2..f85f483e8 100644
--- a/Model/Service/Sync/BulkPublisherInterface.php
+++ b/Model/Service/Sync/BulkPublisherInterface.php
@@ -40,8 +40,8 @@ interface BulkPublisherInterface
 {
     /**
      * @param int $storeId
-     * @param array $productIds
+     * @param array $entityIds
      * @return void
      */
-    public function execute(int $storeId, array $productIds = []);
+    public function execute(int $storeId, array $entityIds = []);
 }
diff --git a/Model/Service/Sync/Upsert/Category/AsyncBulkConsumer.php b/Model/Service/Sync/Upsert/Category/AsyncBulkConsumer.php
index cccef0fa4..eb9d049e2 100644
--- a/Model/Service/Sync/Upsert/Category/AsyncBulkConsumer.php
+++ b/Model/Service/Sync/Upsert/Category/AsyncBulkConsumer.php
@@ -43,7 +43,7 @@
 use Nosto\NostoException;
 use Nosto\Tagging\Helper\Scope as NostoScopeHelper;
 use Nosto\Tagging\Logger\Logger;
-use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory;
+use Nosto\Tagging\Model\ResourceModel\Magento\Product\CollectionFactory;
 use Nosto\Tagging\Model\Service\Sync\AbstractBulkConsumer;
 use Nosto\Tagging\Model\Service\Sync\Upsert\SyncService;
 
@@ -98,12 +98,12 @@ public function __construct(
      * @throws MemoryOutOfBoundsException
      * @throws NostoException
      */
-    public function doOperation(array $categoryIds, string $storeId)
+    public function doOperation(array $productIds, string $storeId)
     {
         $store = $this->nostoScopeHelper->getStore($storeId);
-        $categoryCollection = $this->collectionFactory->create()
-            ->addIdsToFilter($categoryIds)
+        $productCollection = $this->collectionFactory->create()
+            ->addIdsToFilter($productIds)
             ->addStoreFilter($storeId);
-        $this->syncService->syncProducts($categoryCollection, $store);
+        $this->syncService->syncProducts($productCollection, $store);
     }
 }
diff --git a/Model/Service/Sync/Upsert/Category/BulkPublisher.php b/Model/Service/Sync/Upsert/Category/AsyncBulkPublisher.php
similarity index 97%
rename from Model/Service/Sync/Upsert/Category/BulkPublisher.php
rename to Model/Service/Sync/Upsert/Category/AsyncBulkPublisher.php
index da4548519..87e77bd11 100644
--- a/Model/Service/Sync/Upsert/Category/BulkPublisher.php
+++ b/Model/Service/Sync/Upsert/Category/AsyncBulkPublisher.php
@@ -39,7 +39,7 @@
 use Nosto\Tagging\Model\Service\Sync\AbstractBulkPublisher;
 
 // @codingStandardsIgnoreFile
-class BulkPublisher extends AbstractBulkPublisher
+class AsyncBulkPublisher extends AbstractBulkPublisher
 {
     public const NOSTO_SYNC_MESSAGE_QUEUE = 'nosto_category_sync.update';
     public const BULK_SIZE = 100;
diff --git a/Plugin/CategoryUpdate.php b/Plugin/CategoryUpdate.php
new file mode 100644
index 000000000..d0f88e856
--- /dev/null
+++ b/Plugin/CategoryUpdate.php
@@ -0,0 +1,121 @@
+
+ * @copyright 2020 Nosto Solutions Ltd
+ * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause
+ *
+ */
+
+namespace Nosto\Tagging\Plugin;
+
+use Closure;
+use Magento\Catalog\Model\ResourceModel\Category as MagentoResourceCategory;
+use Magento\Framework\Indexer\IndexerRegistry;
+use Magento\Framework\Model\AbstractModel;
+use Nosto\Tagging\Helper\Scope as NostoHelperScope;
+use Nosto\Tagging\Model\Indexer\CategoryIndexer;
+use Nosto\Tagging\Model\Category\Repository as NostoCategoryRepository;
+use Nosto\Tagging\Logger\Logger as NostoLogger;
+use Nosto\Tagging\Model\ResourceModel\Magento\Category\CollectionBuilder;
+use Nosto\Tagging\Model\Service\Update\CategoryUpdateService;
+
+/**
+ * Plugin for product updates
+ */
+class CategoryUpdate
+{
+    /** @var IndexerRegistry  */
+    private IndexerRegistry $indexerRegistry;
+
+    /** @var CategoryIndexer  */
+    private CategoryIndexer $categoryIndexer;
+
+    /** @var NostoCategoryRepository  */
+    private NostoCategoryRepository $nostoCategoryRepository;
+
+    /** @var NostoLogger */
+    private NostoLogger $logger;
+
+    /** @var CategoryUpdateService */
+    private CategoryUpdateService $categoryUpdateService;
+
+    /** @var NostoHelperScope */
+    private NostoHelperScope $nostoHelperScope;
+
+    /** @var CollectionBuilder */
+    private CollectionBuilder $categoryCollectionBuilder;
+
+    /**
+     * ProductUpdate constructor.
+     * @param IndexerRegistry $indexerRegistry
+     * @param CategoryIndexer $categoryIndexer
+     * @param NostoCategoryRepository $nostoCategoryRepository
+     * @param NostoLogger $logger
+     * @param CategoryUpdateService $categoryUpdateService
+     * @param NostoHelperScope $nostoHelperScope
+     */
+    public function __construct(
+        IndexerRegistry                $indexerRegistry,
+        CategoryIndexer                $categoryIndexer,
+        NostoCategoryRepository        $nostoCategoryRepository,
+        NostoLogger                    $logger,
+        CategoryUpdateService          $categoryUpdateService,
+        NostoHelperScope               $nostoHelperScope,
+        CollectionBuilder              $categoryCollectionBuilder
+    ) {
+        $this->indexerRegistry = $indexerRegistry;
+        $this->categoryIndexer = $categoryIndexer;
+        $this->nostoCategoryRepository = $nostoCategoryRepository;
+        $this->logger = $logger;
+        $this->categoryUpdateService = $categoryUpdateService;
+        $this->nostoHelperScope = $nostoHelperScope;
+        $this->categoryCollectionBuilder = $categoryCollectionBuilder;
+    }
+
+    public function aroundSave(
+        MagentoResourceCategory $resourceCategory,
+        Closure $proceed,
+        AbstractModel $category
+    ) {
+        $storeIds = $category->getStoreIds();
+        $categoryCollection = $this->categoryCollectionBuilder->withIds([$category->getId()])->build();
+
+         foreach ($storeIds as $storeId) {
+            $store = $this->nostoHelperScope->getStore($storeId);
+            $this->categoryUpdateService->addCollectionToUpdateMessageQueue($categoryCollection, $store);
+        }
+
+
+        return $proceed($category);
+
+    }
+
+}
diff --git a/etc/di.xml b/etc/di.xml
index 622e4ec52..972223b98 100644
--- a/etc/di.xml
+++ b/etc/di.xml
@@ -49,9 +49,13 @@
     
     
     
+    
     
     
-        
+        
+    
+    
+        
     
     
         
@@ -201,6 +205,9 @@
             
                 nosto_index_product
             
+            
+                nosto_index_category
+            
         
     
     
@@ -224,7 +231,7 @@
             
         
     
-    
+    
         
             
                 Nosto\Tagging\Model\Service\Sync\Upsert\Category\AsyncBulkConsumer
@@ -259,10 +266,7 @@
     
         
             
-                Nosto\Tagging\Model\Service\Sync\Upsert\Category\BulkPublisher
-            
-            
-                Nosto\Tagging\Model\Service\Sync\Delete\AsyncBulkPublisher
+                Nosto\Tagging\Model\Service\Sync\Upsert\Category\AsyncBulkPublisher
             
             500
         
diff --git a/etc/queue_consumer.xml b/etc/queue_consumer.xml
index fb36407aa..693b6be36 100644
--- a/etc/queue_consumer.xml
+++ b/etc/queue_consumer.xml
@@ -50,7 +50,7 @@
     
 
diff --git a/etc/queue_topology.xml b/etc/queue_topology.xml
index 2b929aa7b..3ab1ecee0 100644
--- a/etc/queue_topology.xml
+++ b/etc/queue_topology.xml
@@ -38,5 +38,6 @@
     
         
         
+        
     
 

From 7a2d8ee3eadab1468adebf5c514a31cc95eb23ff Mon Sep 17 00:00:00 2001
From: ugljesaspx 
Date: Fri, 1 Nov 2024 14:20:23 +0100
Subject: [PATCH 14/17] Adapat code for category reindex

---
 Model/Category/Repository.php | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/Model/Category/Repository.php b/Model/Category/Repository.php
index 156a292c0..95906df89 100644
--- a/Model/Category/Repository.php
+++ b/Model/Category/Repository.php
@@ -132,8 +132,7 @@ public function resolveParentCategoryIds(CategoryInterface $category): ?array
 
         $parentCategoryIds = null;
         if ($category->getLevel() >= 1) {
-            $parentCategoryIds = $this->getCategoryParentIds($category['path']);
-
+            $parentCategoryIds = $this->getCategoryParentIds($category->getPath());
             $this->saveParentIdsToCache($category, $parentCategoryIds);
         }
 

From ee3b5b5a24aa21c8f4f89480ae13bf1a8a7d993c Mon Sep 17 00:00:00 2001
From: ugljesaspx 
Date: Fri, 1 Nov 2024 15:21:02 +0100
Subject: [PATCH 15/17] Adapat code for category reindex

---
 Model/Category/Repository.php                 | 54 ++++++++++---------
 .../Service/Update/CategoryUpdateService.php  |  1 +
 etc/indexer.xml                               |  2 +-
 3 files changed, 30 insertions(+), 27 deletions(-)

diff --git a/Model/Category/Repository.php b/Model/Category/Repository.php
index 95906df89..5db875c75 100644
--- a/Model/Category/Repository.php
+++ b/Model/Category/Repository.php
@@ -85,34 +85,11 @@ public function getByIds(array $ids)
     }
 
     /**
-     * @param Store $store
-     * @param array $categoryIds
-     *
-     * @return CategoryCollection
-     * @throws LocalizedException
-     */
-    public function getCategoryCollectionQuery(Store $store, array $categoryIds = [])
-    {
-        $categories = $this->categoryCollectionFactory->create()
-            ->distinct(true)
-            ->addNameToResult()
-            ->setStoreId($store->getId())
-            ->addUrlRewriteToResult()
-            ->addAttributeToFilter('level', ['gt' => 1])
-            ->addAttributeToSelect(array_merge(['name', 'is_active', 'include_in_menu']))
-            ->addOrderField('entity_id');
-
-        if ($categoryIds) {
-            $categories->addAttributeToFilter('entity_id', ['in' => $categoryIds]);
-        }
-
-        return $categories;
-    }
-
-    /**
-     * Gets the parent category ID's for a given category
+     *  Gets the parent category ID's for a given category
      *
+     * @param CategoryInterface $category
      * @return string[]|null
+     *
      * @throws ParentCategoryDisabledException
      */
     public function resolveParentCategoryIds(CategoryInterface $category): ?array
@@ -139,6 +116,31 @@ public function resolveParentCategoryIds(CategoryInterface $category): ?array
         return $parentCategoryIds;
     }
 
+    /**
+     * @param Store $store
+     * @param array $categoryIds
+     *
+     * @return CategoryCollection
+     * @throws LocalizedException
+     */
+    public function getCategoryCollectionQuery(Store $store, array $categoryIds = [])
+    {
+        $categories = $this->categoryCollectionFactory->create()
+            ->distinct(true)
+            ->addNameToResult()
+            ->setStoreId($store->getId())
+            ->addUrlRewriteToResult()
+            ->addAttributeToFilter('level', ['gt' => 1])
+            ->addAttributeToSelect(array_merge(['name', 'is_active', 'include_in_menu']))
+            ->addOrderField('entity_id');
+
+        if ($categoryIds) {
+            $categories->addAttributeToFilter('entity_id', ['in' => $categoryIds]);
+        }
+
+        return $categories;
+    }
+
     /**
      * Get Parent Category IDS
      *
diff --git a/Model/Service/Update/CategoryUpdateService.php b/Model/Service/Update/CategoryUpdateService.php
index 8691dd594..7c598e6ff 100644
--- a/Model/Service/Update/CategoryUpdateService.php
+++ b/Model/Service/Update/CategoryUpdateService.php
@@ -127,6 +127,7 @@ private function toParentCategoryIds(CategoryCollection $collection): array
         /** @var CategoryInterface $category */
         foreach ($collection->getItems() as $category) {
             try {
+                /** @phan-suppress-next-line PhanTypeMismatchArgument */
                 $parents = $this->nostoCategoryRepository->resolveParentCategoryIds($category);
             } catch (ParentCategoryDisabledException $e) {
                 $this->getLogger()->debug($e->getMessage());
diff --git a/etc/indexer.xml b/etc/indexer.xml
index d6a3b0508..6764fa12a 100644
--- a/etc/indexer.xml
+++ b/etc/indexer.xml
@@ -40,7 +40,7 @@
     
     
         Nosto Category Indexer
-        
+        Populates message queue with category ids to be sent to Nosto
         Category
     
 

From c04cd74861221b0f25ef398d01a63a3472d1250d Mon Sep 17 00:00:00 2001
From: Ugljesa Zivaljevic 
Date: Thu, 19 Dec 2024 14:52:23 +0100
Subject: [PATCH 16/17] Prepare for send categories to Nosto

---
 Model/Category/Repository.php                 |  5 ++-
 .../Magento/Category/Collection.php           |  2 -
 Model/Service/Sync/AbstractBulkConsumer.php   |  8 +++-
 Model/Service/Sync/AbstractBulkPublisher.php  | 45 ++++++++++++++-----
 .../Upsert/Category/AsyncBulkConsumer.php     | 16 +++----
 Model/Service/Sync/Upsert/SyncService.php     | 30 +++++++++++++
 .../Service/Update/CategoryUpdateService.php  | 10 ++++-
 Observer/Order/Save.php                       |  1 +
 8 files changed, 91 insertions(+), 26 deletions(-)

diff --git a/Model/Category/Repository.php b/Model/Category/Repository.php
index 5db875c75..d5bd9e482 100644
--- a/Model/Category/Repository.php
+++ b/Model/Category/Repository.php
@@ -81,7 +81,10 @@ public function __construct(
      */
     public function getByIds(array $ids)
     {
-        //@TODO implement
+        $searchCriteria = $this->searchCriteriaBuilder
+            ->addFilter('entity_id', $ids, 'in')
+            ->create();
+        return $this->categoryRepository->getList($searchCriteria);
     }
 
     /**
diff --git a/Model/ResourceModel/Magento/Category/Collection.php b/Model/ResourceModel/Magento/Category/Collection.php
index 44703b881..4962dffd6 100644
--- a/Model/ResourceModel/Magento/Category/Collection.php
+++ b/Model/ResourceModel/Magento/Category/Collection.php
@@ -43,7 +43,6 @@ class Collection extends MagentoCategoryCollection
 {
     /**
      * @return Collection
-     * @throws LocalizedException
      */
     public function addActiveFilter(): Collection
     {
@@ -53,7 +52,6 @@ public function addActiveFilter(): Collection
     /**
      * @param array $ids
      * @return Collection
-     * @throws LocalizedException
      */
     public function addIdsToFilter(array $ids): Collection
     {
diff --git a/Model/Service/Sync/AbstractBulkConsumer.php b/Model/Service/Sync/AbstractBulkConsumer.php
index 0f3c51abc..4745aca27 100644
--- a/Model/Service/Sync/AbstractBulkConsumer.php
+++ b/Model/Service/Sync/AbstractBulkConsumer.php
@@ -90,11 +90,15 @@ public function processOperation(OperationInterface $operation)
     {
         $serializedData = $operation->getSerializedData();
         $unserializedData = $this->jsonHelper->jsonDecode($serializedData);
-        $productIds = $unserializedData['product_ids'];
+
+        $productIds = $unserializedData['product_ids'] ?? null;
+        $categoryIds = $unserializedData['category_ids'] ?? null;
         $storeId = $unserializedData['store_id'];
         try {
+            $idsToProcess = $productIds ?: $categoryIds;
+
             $this->storeEmulation->startEnvironmentEmulation((int)$storeId);
-            $this->doOperation($productIds, $storeId);
+            $this->doOperation($idsToProcess, $storeId);
             /**
              * Argument is of type string but array is expected
              */
diff --git a/Model/Service/Sync/AbstractBulkPublisher.php b/Model/Service/Sync/AbstractBulkPublisher.php
index efbf6e81c..c0a706d66 100644
--- a/Model/Service/Sync/AbstractBulkPublisher.php
+++ b/Model/Service/Sync/AbstractBulkPublisher.php
@@ -123,34 +123,52 @@ private function publishCollectionToMessageQueue(
             );
             return;
         }
-        $productIdsChunks = array_chunk($entityIds, $this->getBulkSize());
+
         $bulkUuid = $this->identityService->generateId();
-        /**
-         * Argument is of type string but array is expected
-         */
-        /** @phan-suppress-next-line PhanTypeMismatchArgumentProbablyReal */
-        $bulkDescription = __('Sync ' . count($entityIds) . ' Nosto products');
+        $bulkDescription = __('Sync ' . count($entityIds) . ' Nosto entities');
         $operationsData = [];
-        foreach ($productIdsChunks as $productIdsChunk) {
-            $operationsData[] = $this->buildOperationData(
-                $storeId,
-                $productIdsChunk,
-                $bulkUuid
-            );
+
+        // TODO: @Ugljesa Check the code.
+        if (isset($entityIds['entity']) && $entityIds['entity'] === 'category') {
+            // Process Categories.
+            $categoryIdsChunks = array_chunk($entityIds['categoryIds'], $this->getBulkSize());
+
+            foreach ($categoryIdsChunks as $categoryIdsChunk) {
+                $operationsData[] = $this->buildOperationData(
+                    $storeId,
+                    [],
+                    $categoryIdsChunk,
+                    $bulkUuid
+                );
+            }
+        } else {
+            // Process Products.
+            $productIdsChunks = array_chunk($entityIds, $this->getBulkSize());
+            foreach ($productIdsChunks as $productIdsChunk) {
+                $operationsData[] = $this->buildOperationData(
+                    $storeId,
+                    $productIdsChunk,
+                    [],
+                    $bulkUuid
+                );
+            }
         }
 
         $operations = [];
         foreach ($operationsData as $operationData) {
             $operations[] = $this->operationFactory->create($operationData);
         }
+
         if (empty($operations)) {
             return;
         }
+
         $result = $this->bulkManagement->scheduleBulk(
             $bulkUuid,
             $operations,
             $bulkDescription
         );
+
         if (!$result) {
             $msg = 'Something went wrong while publishing bulk to RabbitMQ. Please check your connection';
             /** @phan-suppress-next-line PhanTypeMismatchArgumentProbablyReal */
@@ -190,17 +208,20 @@ abstract public function getMetaData(): string;
      * Build asynchronous operation data
      * @param int $storeId
      * @param array $productIds
+     * @param array $categoryIdsChunks
      * @param string $bulkUuid
      * @return array
      */
     private function buildOperationData(
         int $storeId,
         array $productIds,
+        array $categoryIdsChunks,
         string $bulkUuid
     ) {
         $dataToEncode = [
             'meta_information' => $this->getMetaData(),
             'product_ids' => $productIds,
+            'category_ids' => $categoryIdsChunks,
             'store_id' => $storeId
         ];
         return [
diff --git a/Model/Service/Sync/Upsert/Category/AsyncBulkConsumer.php b/Model/Service/Sync/Upsert/Category/AsyncBulkConsumer.php
index eb9d049e2..273f00444 100644
--- a/Model/Service/Sync/Upsert/Category/AsyncBulkConsumer.php
+++ b/Model/Service/Sync/Upsert/Category/AsyncBulkConsumer.php
@@ -37,13 +37,14 @@
 namespace Nosto\Tagging\Model\Service\Sync\Upsert\Category;
 
 use Magento\Framework\EntityManager\EntityManager;
+use Magento\Framework\Exception\LocalizedException;
 use Magento\Framework\Json\Helper\Data as JsonHelper;
 use Magento\Store\Model\App\Emulation;
 use Nosto\Exception\MemoryOutOfBoundsException;
 use Nosto\NostoException;
 use Nosto\Tagging\Helper\Scope as NostoScopeHelper;
 use Nosto\Tagging\Logger\Logger;
-use Nosto\Tagging\Model\ResourceModel\Magento\Product\CollectionFactory;
+use Nosto\Tagging\Model\ResourceModel\Magento\Category\CollectionFactory;
 use Nosto\Tagging\Model\Service\Sync\AbstractBulkConsumer;
 use Nosto\Tagging\Model\Service\Sync\Upsert\SyncService;
 
@@ -95,15 +96,14 @@ public function __construct(
 
     /**
      * @inheritDoc
-     * @throws MemoryOutOfBoundsException
-     * @throws NostoException
      */
-    public function doOperation(array $productIds, string $storeId)
+    public function doOperation(array $categoryIds, string $storeId)
     {
+        // TODO: Add store.
         $store = $this->nostoScopeHelper->getStore($storeId);
-        $productCollection = $this->collectionFactory->create()
-            ->addIdsToFilter($productIds)
-            ->addStoreFilter($storeId);
-        $this->syncService->syncProducts($productCollection, $store);
+        $categoryCollection = $this->collectionFactory->create()
+            ->addIdsToFilter($categoryIds);
+
+        $this->syncService->syncCategories($categoryCollection, $store);
     }
 }
diff --git a/Model/Service/Sync/Upsert/SyncService.php b/Model/Service/Sync/Upsert/SyncService.php
index fda89b5ae..b8b23bea0 100644
--- a/Model/Service/Sync/Upsert/SyncService.php
+++ b/Model/Service/Sync/Upsert/SyncService.php
@@ -40,6 +40,7 @@
 use Magento\Catalog\Model\Product;
 use Magento\Store\Model\Store;
 use Nosto\Exception\MemoryOutOfBoundsException;
+use Nosto\Tagging\Model\ResourceModel\Magento\Category\Collection as CategoryCollection;
 use Nosto\NostoException;
 use Nosto\Operation\UpsertProduct;
 use Nosto\Request\Http\Exception\AbstractHttpException;
@@ -180,4 +181,33 @@ public function syncProducts(ProductCollection $collection, Store $store)
         }
         $this->logBenchmarkSummary(self::BENCHMARK_SYNC_NAME, $store, $this);
     }
+
+    /**
+     * @throws NostoException
+     * @throws MemoryOutOfBoundsException
+     */
+    public function syncCategories(CategoryCollection $collection, Store $store)
+    {
+        if (!$this->nostoDataHelper->isProductUpdatesEnabled($store)) {
+            $this->logDebugWithStore(
+                'Nosto Category sync is disabled',
+                $store
+            );
+            return;
+        }
+        $account = $this->nostoHelperAccount->findAccount($store);
+        $this->startBenchmark('nosto_category_upsert', self::BENCHMARK_SYNC_BREAKPOINT);
+        $index = 0;
+        $collection->setPageSize($this->apiBatchSize);
+        $iterator = new PagingIterator($collection);
+        /** @var CategoryCollection $page */
+        foreach ($iterator as $page) {
+            $categoryIdsInBatch = [];
+            $this->checkMemoryConsumption('category sync');
+
+            die;
+
+        }
+
+    }
 }
diff --git a/Model/Service/Update/CategoryUpdateService.php b/Model/Service/Update/CategoryUpdateService.php
index 7c598e6ff..edf15f299 100644
--- a/Model/Service/Update/CategoryUpdateService.php
+++ b/Model/Service/Update/CategoryUpdateService.php
@@ -52,6 +52,8 @@
 
 class CategoryUpdateService extends AbstractService
 {
+    const CATEGORY_SERIVCE = 'category';
+
     /** @var NostoCategoryRepository $nostoCategoryRepository */
     private NostoCategoryRepository $nostoCategoryRepository;
 
@@ -113,7 +115,12 @@ public function addCollectionToUpdateMessageQueue(CategoryCollection $collection
         );
         /** @var CategoryCollection $page */
         foreach ($iterator as $page) {
-            $this->upsertBulkPublisher->execute($store->getId(), $this->toParentCategoryIds($page));
+            $data = [
+                'entity' => self::CATEGORY_SERIVCE,
+                'categoryIds' => $this->toParentCategoryIds($page)
+            ];
+
+            $this->upsertBulkPublisher->execute($store->getId(), $data);
         }
     }
 
@@ -125,6 +132,7 @@ private function toParentCategoryIds(CategoryCollection $collection): array
     {
         $categoryIds = [];
         /** @var CategoryInterface $category */
+        // TODO: @ugljesa Check instanceof.
         foreach ($collection->getItems() as $category) {
             try {
                 /** @phan-suppress-next-line PhanTypeMismatchArgument */
diff --git a/Observer/Order/Save.php b/Observer/Order/Save.php
index 37589c72f..e690fca7b 100644
--- a/Observer/Order/Save.php
+++ b/Observer/Order/Save.php
@@ -46,6 +46,7 @@
 use Magento\Framework\Module\Manager as ModuleManager;
 use Magento\Sales\Model\Order;
 use Magento\Store\Model\Store;
+use Nosto\Model\Order\Buyer;
 use Nosto\Model\Order\Order as NostoOrder;
 use Nosto\Operation\Order\OrderCreate as NostoOrderCreate;
 use Nosto\Operation\Order\OrderStatus as NostoOrderUpdate;

From ee349e7792abe0214536fd22b2bacb8188e95dc7 Mon Sep 17 00:00:00 2001
From: makso8makso 
Date: Thu, 30 Jan 2025 16:52:36 +0100
Subject: [PATCH 17/17] implement category sending to nosto

---
 Model/Service/Sync/Upsert/SyncService.php |  80 +++++++++--
 Operation/CategoryUpdateOnNosto.php       | 168 ++++++++++++++++++++++
 2 files changed, 240 insertions(+), 8 deletions(-)
 create mode 100644 Operation/CategoryUpdateOnNosto.php

diff --git a/Model/Service/Sync/Upsert/SyncService.php b/Model/Service/Sync/Upsert/SyncService.php
index b8b23bea0..cae34affb 100644
--- a/Model/Service/Sync/Upsert/SyncService.php
+++ b/Model/Service/Sync/Upsert/SyncService.php
@@ -51,9 +51,12 @@
 use Nosto\Tagging\Model\ResourceModel\Magento\Product\Collection as ProductCollection;
 use Nosto\Tagging\Model\Service\AbstractService;
 use Nosto\Tagging\Model\Service\Cache\CacheService;
+use Nosto\Tagging\Model\Service\Product\Category\CategoryServiceInterface as NostoCategoryService;
 use Nosto\Tagging\Model\Service\Product\ProductServiceInterface;
 use Nosto\Tagging\Util\PagingIterator;
 use Nosto\Tagging\Model\Product\Repository as ProductRepository;
+use Nosto\Tagging\Model\Category\Repository as CategoryRepository;
+use Nosto\Tagging\Model\Operation\CategoryUpdateOnNosto;
 
 class SyncService extends AbstractService
 {
@@ -84,6 +87,11 @@ class SyncService extends AbstractService
     /** @var ProductRepository */
     private ProductRepository $productRepository;
 
+    private CategoryRepository $categoryRepository;
+
+    /** @var NostoCategoryService */
+    private NostoCategoryService $nostoCategoryService;
+
     /**
      * Sync constructor.
      * @param NostoHelperAccount $nostoHelperAccount
@@ -93,6 +101,8 @@ class SyncService extends AbstractService
      * @param ProductServiceInterface $productService
      * @param CacheService $cacheService
      * @param ProductRepository $productRepository
+     * @param CategoryRepository $categoryRepository
+     * @param CategoryServiceInterface $nostoCategoryService
      * @param $apiBatchSize
      * @param $apiTimeout
      */
@@ -104,6 +114,8 @@ public function __construct(
         ProductServiceInterface $productService,
         CacheService $cacheService,
         ProductRepository $productRepository,
+        CategoryRepository $categoryRepository,
+        NostoCategoryService $nostoCategoryService,
         $apiBatchSize,
         $apiTimeout
     ) {
@@ -114,6 +126,8 @@ public function __construct(
         $this->nostoDataHelper = $nostoDataHelper;
         $this->cacheService = $cacheService;
         $this->productRepository = $productRepository;
+        $this->categoryRepository = $categoryRepository;
+        $this->nostoCategoryService = $nostoCategoryService;
         $this->apiBatchSize = $apiBatchSize;
         $this->apiTimeout = $apiTimeout;
     }
@@ -188,26 +202,76 @@ public function syncProducts(ProductCollection $collection, Store $store)
      */
     public function syncCategories(CategoryCollection $collection, Store $store)
     {
-        if (!$this->nostoDataHelper->isProductUpdatesEnabled($store)) {
-            $this->logDebugWithStore(
-                'Nosto Category sync is disabled',
-                $store
-            );
-            return;
-        }
+//        if (!$this->nostoDataHelper->isProductUpdatesEnabled($store)) {
+//            $this->logDebugWithStore(
+//                'Nosto Category sync is disabled',
+//                $store
+//            );
+//            return;
+//        }
+        $categoryIdsInBatch = [];
         $account = $this->nostoHelperAccount->findAccount($store);
         $this->startBenchmark('nosto_category_upsert', self::BENCHMARK_SYNC_BREAKPOINT);
         $index = 0;
         $collection->setPageSize($this->apiBatchSize);
         $iterator = new PagingIterator($collection);
+
         /** @var CategoryCollection $page */
         foreach ($iterator as $page) {
-            $categoryIdsInBatch = [];
             $this->checkMemoryConsumption('category sync');
 
+            // todo: send to nosto customer
+            foreach ($page->getData() as $category) {
+                $categoryDb = $this->categoryRepository->getById($category['entity_id']);
+                $categoryIdsInBatch[] = $category['entity_id'];
+                $id = $category['entity_id'];
+                $name = explode("/", $categoryDb->getName());
+                $name = end($name);
+                $parentId = $categoryDb->getParentId();
+                $urlPath = $this->getUrlPath($categoryDb, $store);
+                $fullName = $categoryDb->getName();
+                $available = $categoryDb->getIsActive() ?? false;
+                try {
+                    (new CategoryUpdateOnNosto($account, $store->getCurrentUrl(), $id, $name, $parentId, $urlPath, $fullName, $available))->execute();
+                } catch (Exception $e) {
+                    $this->logger->logDebugWithStore(sprintf(
+                        'Failed to update Nosto category %s with %s id',
+                        $name,
+                        $id
+                    ));
+                }
+            }
+
+            $this->logDebugWithStore(
+                sprintf(
+                    'Nosto category sync with ids - %s',
+                    implode(',', $categoryIdsInBatch)
+                ),
+                $store
+            );
+
             die;
 
         }
 
     }
+
+    private function getUrlPath($category, Store $store)
+    {
+        $nostoCategory = '';
+        try {
+            $data = [];
+            $path = $category->getPath();
+            $data[] = $category->getName();
+            $nostoCategory = count($data) ? '/' . implode('/', $data) : '';
+        } catch (Exception $e) {
+            $this->logDebugWithStore($e, $store);
+        }
+
+        if (empty($nostoCategory)) {
+            $nostoCategory = null;
+        }
+
+        return $nostoCategory ? trim($nostoCategory) : $nostoCategory;
+    }
 }
diff --git a/Operation/CategoryUpdateOnNosto.php b/Operation/CategoryUpdateOnNosto.php
new file mode 100644
index 000000000..961fb7a31
--- /dev/null
+++ b/Operation/CategoryUpdateOnNosto.php
@@ -0,0 +1,168 @@
+
+ * @copyright 2020 Nosto Solutions Ltd
+ * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause
+ *
+ */
+namespace Nosto\Tagging\Model\Operation;
+
+use Nosto\Operation\AbstractGraphQLOperation;
+use Nosto\Types\Signup\AccountInterface;
+use Nosto\Result\Graphql\Session\SessionResultHandler;
+
+class CategoryUpdateOnNosto extends AbstractGraphQLOperation
+{
+    /**
+     * @var AccountInterface Nosto configuration.
+     */
+    protected $account;
+
+    /**
+     * @var string active domain
+     */
+    protected $activeDomain;
+
+    /**
+     * @var string $id
+     */
+    private string $id;
+
+    /**
+     * @var string $name
+     */
+    private string $name;
+
+    /**
+     * @var string $parentId
+     */
+    private string $parentId;
+
+    /**
+     * @var string $urlPath
+     */
+    private string $urlPath;
+
+    /**
+     * @var string $fullName
+     */
+    private string $fullName;
+
+    /**
+     * @var boolean
+     */
+    private bool $available;
+
+    /**
+     * @var string
+     */
+    private string $referer;
+
+    /**
+     * NewSession constructor.
+     * @param AccountInterface $account
+     * @param string $id
+     * @param string $name
+     * @param string $parentId
+     * @param string $urlPath
+     * @param string $fullName
+     * @param boolean $available
+     */
+    public function __construct(AccountInterface $account, string $referer, string $id, string $name, string $parentId, string $urlPath, string $fullName, bool $available, string $activeDomain = '')
+    {
+        $this->referer = $referer;
+        $this->id = $id;
+        $this->name = $name;
+        $this->parentId = $parentId;
+        $this->urlPath = $urlPath;
+        $this->fullName = $fullName;
+        $this->available = $available;
+
+        parent::__construct($account, $activeDomain);
+    }
+
+    /**
+     * @return string
+     */
+    public function getQuery(): string
+    {
+        return <<id}",
+                        name: "{$this->name}",
+                        parentId: "{$this->parentId}",
+                        urlPath: "{$this->urlPath}",
+                        fullName: "{$this->fullName}",
+                        available: "{$this->available}"
+                }
+              ]
+          ) {
+            categoryResult {
+            errors {
+              field
+              message
+            }
+            category {
+              id
+              name
+              parentId
+              urlPath
+              fullName
+              available
+                }
+                }
+            }
+        }
+QUERY;
+    }
+
+    /**
+     * @return array
+     */
+    public function getVariables(): array
+    {
+        return [
+            "referer" => $this->referer
+        ];
+    }
+
+    /**
+     * @return SessionResultHandler
+     */
+    protected function getResultHandler(): SessionResultHandler
+    {
+        return new SessionResultHandler();
+    }
+}