diff --git a/Api/Data/FeedInterface.php b/Api/Data/FeedInterface.php
new file mode 100644
index 0000000..1b52258
--- /dev/null
+++ b/Api/Data/FeedInterface.php
@@ -0,0 +1,86 @@
+resultPageFactory = $resultPageFactory;
+ }
+
+ public function execute()
+ {
+ $resultPage = $this->resultPageFactory->create();
+ $resultPage->getConfig()->getTitle()->prepend((__('Google Shopping Feeds')));
+
+ return $resultPage;
+ }
+}
\ No newline at end of file
diff --git a/Model/Feed.php b/Model/Feed.php
new file mode 100644
index 0000000..9009289
--- /dev/null
+++ b/Model/Feed.php
@@ -0,0 +1,90 @@
+getData(self::FILENAME);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function setFileName(string $fileName): FeedInterface
+ {
+ return $this->setData(self::FILENAME, $fileName);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getPath(): string
+ {
+ return $this->getData(self::PATH);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function setPath(string $path): FeedInterface
+ {
+ return $this->setData(self::PATH, $path);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getLink(): string
+ {
+ return $this->getData(self::LINK);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function setLink(string $link): FeedInterface
+ {
+ return $this->setData(self::LINK, $link);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getLastGenerated(): string
+ {
+ return $this->getData(self::LAST_GENERATED);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function setLastGenerated(string $lastGenerated): FeedInterface
+ {
+ return $this->setData(self::LAST_GENERATED, $lastGenerated);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getStore(): string
+ {
+ return $this->getData(self::STORE);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function setStore(string $store): FeedInterface
+ {
+ return $this->setData(self::STORE, $store);
+ }
+}
\ No newline at end of file
diff --git a/Model/FeedRepository.php b/Model/FeedRepository.php
new file mode 100644
index 0000000..ec5ecdf
--- /dev/null
+++ b/Model/FeedRepository.php
@@ -0,0 +1,57 @@
+fileReaderProvider = $fileReaderProvider;
+ $this->feedFactory = $feedFactory;
+ }
+
+ /**
+ * @inheritDoc
+ * @throws NoSuchEntityException
+ * @throws LocalizedException
+ */
+ public function getList(): array
+ {
+ $fileReader = $this->fileReaderProvider->get();
+
+ try {
+ $files = $fileReader->read();
+ } catch (LocalizedException $e) {
+ throw new LocalizedException(__($e->getMessage()));
+ }
+
+ $feeds = [];
+
+ foreach ($files as $file) {
+ /** @var Feed $feed */
+ $feed = $this->feedFactory->create();
+
+ $feed->setFileName($file['fileName']);
+ $feed->setPath($file['path']);
+ $feed->setLink($file['link']);
+ $feed->setLastGenerated($file['fileGenerationTime']);
+ $feed->setStore($file['store']);
+
+ $feeds[] = $feed->toArray();
+ }
+
+
+ return $feeds;
+ }
+}
\ No newline at end of file
diff --git a/README.md b/README.md
index 9d05db3..52ff6ae 100644
--- a/README.md
+++ b/README.md
@@ -80,3 +80,7 @@ Performs iteration on all products provided by this collection provider `\RunAsR
### Add new attribute to feed
1. Create new attribute data provider. @see interface `\RunAsRoot\GoogleShoppingFeed\DataProvider\AttributeHandlers\AttributeHandlerInterface`.
2. Add configuration for new attribute in `\RunAsRoot\GoogleShoppingFeed\Enum\AttributesToImportEnumInterface::ATTRIBUTES`.
+
+## Google Shopping Feeds Grid
+Generated feeds could be reviewed inside Admin Backoffice
+* Navigate to Marketing -> run_as_root -> Google Shopping Feed
\ No newline at end of file
diff --git a/Reader/FileReader.php b/Reader/FileReader.php
new file mode 100644
index 0000000..a017323
--- /dev/null
+++ b/Reader/FileReader.php
@@ -0,0 +1,87 @@
+urlBuilder = $urlBuilder;
+ $this->filesystemIteratorFactory = $filesystemIteratorFactory;
+ $this->dateTime = $dateTime;
+ }
+
+ /**
+ * @throws NoSuchEntityException
+ * @throws LocalizedException
+ */
+ public function read(): array
+ {
+ if (empty($this->destination)) {
+ throw new LocalizedException(
+ new Phrase('The destination is not set')
+ );
+ }
+
+ try {
+ $dir = $this->filesystemIteratorFactory->create([
+ 'path' => DirectoryList::MEDIA . DIRECTORY_SEPARATOR . $this->destination,
+ 'flags' => FilesystemIterator::SKIP_DOTS
+ ]);
+ } catch (\UnexpectedValueException $exception) {
+ return [];
+ }
+
+ $result = [];
+ $storeMediaUrl = $this->urlBuilder->getBaseUrl(['_type' => UrlInterface::URL_TYPE_MEDIA]);
+
+ while ($dir->valid()) {
+ if (!$dir->isDir()) {
+ $this->dateTime->setTimestamp($dir->getMTime());
+ $fileGenerationTime = $this->dateTime->format('Y-m-d H:i:s');
+ $fileName = $dir->getFilename();
+ $stores = null;
+ preg_match('/_store_(\w+)_feed/', $fileName, $stores);
+ $result[] = [
+ 'path' => $dir->getPath() . DIRECTORY_SEPARATOR . $fileName,
+ 'fileGenerationTime' => $fileGenerationTime,
+ 'link' => $storeMediaUrl . $dir,
+ 'fileName' => $fileName,
+ 'store' => $stores[1] ?? 'default'
+ ];
+ }
+
+ $dir->next();
+ }
+
+ return $result;
+ }
+
+ public function setDestination(string $value): FileReader
+ {
+ $this->destination = $value;
+ return $this;
+ }
+}
\ No newline at end of file
diff --git a/Reader/FileReaderProvider.php b/Reader/FileReaderProvider.php
new file mode 100644
index 0000000..c97d168
--- /dev/null
+++ b/Reader/FileReaderProvider.php
@@ -0,0 +1,24 @@
+fileReaderFactory = $fileReaderFactory;
+ }
+
+ public function get(): FileReader
+ {
+ $fileReader = $this->fileReaderFactory->create();
+ $fileReader->setDestination(XmlFileWriterProvider::DIRECTORY_PATH);
+
+ return $fileReader;
+ }
+}
\ No newline at end of file
diff --git a/Test/Unit/Converter/ArrayToXmlConverterTest.php b/Test/Unit/Converter/ArrayToXmlConverterTest.php
index 731739f..4e88024 100644
--- a/Test/Unit/Converter/ArrayToXmlConverterTest.php
+++ b/Test/Unit/Converter/ArrayToXmlConverterTest.php
@@ -33,16 +33,16 @@ private function getProductRow(): array
{
return [
[
- 'category_url' => 'https://app.seidenland.test/bettdecken/naturhaardecken',
+ 'category_url' => 'https://app.default.test/bettdecken/naturhaardecken',
'color' => null,
'description' => 'product-description',
'ean' => '4000863525821',
'gender' => null,
'item_group_id' => 'FAN-53KBBW01V0011',
- 'image_link' => 'https://app.seidenland.test/media/catalog/product/cache/775fcb5986783027a0f3aac116b4fff3/c/a/cashmere-duo-winterdecke-53kbbw01v0009-frankenstolz-2_2.jpg',
+ 'image_link' => 'https://app.default.test/media/catalog/product/cache/775fcb5986783027a0f3aac116b4fff3/c/a/cashmere-duo-winterdecke-53kbbw01v0009-frankenstolz-2_2.jpg',
'additional_image_link' => [
- 0 => 'https://app.seidenland.test/media/catalog/product/cache/775fcb5986783027a0f3aac116b4fff3/o/e/oeko-tex-zertifizierte-schadstoffkontrollierte-bettwaren-frankenstolz_3_2_1_2_2_4_1_1_3_2_2_2_2.jpg',
- 1 => 'https://app.seidenland.test/media/catalog/product/cache/775fcb5986783027a0f3aac116b4fff3/f/a/fan-bettwaren-made-in-germany_2_2_1_2_2_4_1_1_3_2_2_2_2.jpg'
+ 0 => 'https://app.default.test/media/catalog/product/cache/775fcb5986783027a0f3aac116b4fff3/o/e/oeko-tex-zertifizierte-schadstoffkontrollierte-bettwaren-frankenstolz_3_2_1_2_2_4_1_1_3_2_2_2_2.jpg',
+ 1 => 'https://app.default.test/media/catalog/product/cache/775fcb5986783027a0f3aac116b4fff3/f/a/fan-bettwaren-made-in-germany_2_2_1_2_2_4_1_1_3_2_2_2_2.jpg'
],
'is_in_stock' => 'in_stock',
'manufacturer' => 'f.a.n. Frankenstolz',
@@ -81,7 +81,7 @@ private function getProductRow(): array
],
],
'sku' => 'FAN-53KBBW01V0011-155/220',
- 'url' => 'https://app.seidenland.test/cashmere-90-duo-bettdecke-extra-warm-frankenstolz?tec_size_bettwaren_config=1113',
+ 'url' => 'https://app.default.test/cashmere-90-duo-bettdecke-extra-warm-frankenstolz?tec_size_bettwaren_config=1113',
]
];
}
@@ -97,8 +97,8 @@ private function getExpectedResult(): string
-
-
+
+
@@ -133,7 +133,7 @@ private function getExpectedResult(): string
-
+
diff --git a/Test/Unit/DataProvider/AttributeHandlers/AdditionalImageLinkProviderTest.php b/Test/Unit/DataProvider/AttributeHandlers/AdditionalImageLinkProviderTest.php
index 651c6f1..2f566a8 100644
--- a/Test/Unit/DataProvider/AttributeHandlers/AdditionalImageLinkProviderTest.php
+++ b/Test/Unit/DataProvider/AttributeHandlers/AdditionalImageLinkProviderTest.php
@@ -99,8 +99,8 @@ public function testGet(): void
->method('getFile')
->willReturn($productImageThree);
- $productImageLinkTwo = 'https://app.seidenland.test/media/catalog/product/o/e/image-two.jpg';
- $productImageLinkThree = 'https://app.seidenland.test/media/catalog/product/o/e/image-three.jpg';
+ $productImageLinkTwo = 'https://app.default.test/media/catalog/product/o/e/image-two.jpg';
+ $productImageLinkThree = 'https://app.default.test/media/catalog/product/o/e/image-three.jpg';
$this->productImageUrlProviderMock
->expects($this->exactly(2))
diff --git a/Test/Unit/DataProvider/AttributeHandlers/CategoryUrlProviderTest.php b/Test/Unit/DataProvider/AttributeHandlers/CategoryUrlProviderTest.php
index 57dd852..db8b422 100644
--- a/Test/Unit/DataProvider/AttributeHandlers/CategoryUrlProviderTest.php
+++ b/Test/Unit/DataProvider/AttributeHandlers/CategoryUrlProviderTest.php
@@ -82,7 +82,7 @@ public function testGet(): void
->method('getRequestPath')
->willReturn($requestPath);
- $categoryUrl = 'https://www.seidenland.de/waschen-pflegen/speick-naturkosmetik';
+ $categoryUrl = 'https://www.default/waschen-pflegen/speick-naturkosmetik';
$this->urlMock
->expects($this->once())
->method('getDirectUrl')
diff --git a/Test/Unit/DataProvider/AttributeHandlers/ImageLinkProviderTest.php b/Test/Unit/DataProvider/AttributeHandlers/ImageLinkProviderTest.php
index 532339d..10f3c10 100644
--- a/Test/Unit/DataProvider/AttributeHandlers/ImageLinkProviderTest.php
+++ b/Test/Unit/DataProvider/AttributeHandlers/ImageLinkProviderTest.php
@@ -56,7 +56,7 @@ public function testItShouldReturnTheChildProductImageUrl(): void
->method('getImage')
->willReturn($image);
- $productImageLink = 'https://app.seidenland.test/media/catalog/product/o/c/child-product-image.jpg';
+ $productImageLink = 'https://app.default.test/media/catalog/product/o/c/child-product-image.jpg';
$this->productImageUrlProviderMock
->expects($this->once())
@@ -96,7 +96,7 @@ public function testItShouldReturnTheParentProductImageUrl(): void
->method('getImage')
->willReturn($image);
- $productImageLink = 'https://app.seidenland.test/media/catalog/product/o/c/parent-product-image.jpg';
+ $productImageLink = 'https://app.default.test/media/catalog/product/o/c/parent-product-image.jpg';
$this->productImageUrlProviderMock
->expects($this->once())
diff --git a/Test/Unit/DataProvider/ProductImageProviderTest.php b/Test/Unit/DataProvider/ProductImageProviderTest.php
index 44f0480..ca0be4d 100644
--- a/Test/Unit/DataProvider/ProductImageProviderTest.php
+++ b/Test/Unit/DataProvider/ProductImageProviderTest.php
@@ -13,7 +13,7 @@ final class ProductImageProviderTest extends TestCase
{
private ProductImageUrlProvider $sut;
- private string $imageUrl = 'https://app.seidenland.test/media/catalog/product/c/a/image.jpg';
+ private string $imageUrl = 'https://app.default.test/media/catalog/product/c/a/image.jpg';
private string $imagePath = 'c/a/image.jpg';
protected function setUp(): void
diff --git a/Test/Unit/Model/FeedRepositoryTest.php b/Test/Unit/Model/FeedRepositoryTest.php
new file mode 100644
index 0000000..bd4add6
--- /dev/null
+++ b/Test/Unit/Model/FeedRepositoryTest.php
@@ -0,0 +1,94 @@
+fileReaderProviderMock = $this->createMock(FileReaderProvider::class);
+ $this->feedFactoryMock = $this->createMock(FeedFactory::class);
+
+ $this->sut = new FeedRepository($this->fileReaderProviderMock, $this->feedFactoryMock);
+ }
+
+ public function testLocalizedExceptionThrown(): void
+ {
+ $fileReaderMock = $this->createMock(FileReader::class);
+ $this->fileReaderProviderMock->method('get')->willReturn($fileReaderMock);
+
+ $expectedLogMessage = 'The destination is not set';
+
+ $this->fileReaderProviderMock->expects($this->once())
+ ->method('get')
+ ->willThrowException(new LocalizedException(__($expectedLogMessage)));
+
+ $this->expectExceptionMessage($expectedLogMessage);
+
+ $this->sut->getList();
+ }
+
+ public function testGetListEmptyResult(): void
+ {
+ $fileReaderMock = $this->createMock(FileReader::class);
+ $this->fileReaderProviderMock->method('get')->willReturn($fileReaderMock);
+
+ $fileReaderMock->method('read')->willReturn([]);
+
+ $this->assertEquals([], $this->sut->getList());
+ }
+
+ public function testGetList(): void
+ {
+ $fileReaderMock = $this->createMock(FileReader::class);
+ $this->fileReaderProviderMock->method('get')->willReturn($fileReaderMock);
+
+ $file = [
+ 'fileName' => 'base_store_default_feed.xml',
+ 'path' => 'media/run_as_root/feed/base_store_default_feed.xml',
+ 'link' => 'https://local.magento2.com/media/run_as_root/feed/base_store_default_feed.xml',
+ 'fileGenerationTime' => date('Y-m-d H:i:s'),
+ 'store' => 'default'
+ ];
+
+ $files = [
+ $file
+ ];
+
+ $fileReaderMock->method('read')->willReturn($files);
+
+ $feedMock = $this->createMock(Feed::class);
+ $this->feedFactoryMock->method('create')->willReturn($feedMock);
+
+ $feedMock->method('setFileName')->with($file['fileName'])->willReturn($feedMock);
+ $feedMock->method('setPath')->with($file['path'])->willReturn($feedMock);
+ $feedMock->method('setLink')->with($file['link'])->willReturn($feedMock);
+ $feedMock->method('setLastGenerated')->with($file['fileGenerationTime'])->willReturn($feedMock);
+ $feedMock->method('setStore')->with($file['store'])->willReturn($feedMock);
+
+ $feedMock->method('toArray')->willReturn([
+ 'filename' => $file['fileName'],
+ 'path' => $file['path'],
+ 'link' => $file['link'],
+ 'last_generated' => $file['fileGenerationTime'],
+ 'store' => $file['store']
+ ]);
+
+ $this->assertArrayHasKey('link', $this->sut->getList()[0]);
+ }
+}
diff --git a/Test/Unit/Reader/FileReaderProviderTest.php b/Test/Unit/Reader/FileReaderProviderTest.php
new file mode 100644
index 0000000..2093775
--- /dev/null
+++ b/Test/Unit/Reader/FileReaderProviderTest.php
@@ -0,0 +1,40 @@
+fileReaderFactoryMock = $this->createMock(FileReaderFactory::class);
+
+ $this->sut = new FileReaderProvider($this->fileReaderFactoryMock);
+ }
+
+ public function testGet()
+ {
+ $fileReaderMock = $this->createMock(FileReader::class);
+ $destination = 'run_as_root/feed';
+
+ $this->fileReaderFactoryMock
+ ->expects($this->once())
+ ->method('create')
+ ->willReturn($fileReaderMock);
+
+ $fileReaderMock->expects($this->once())
+ ->method('setDestination')
+ ->with($destination)
+ ->willReturnSelf();
+
+ $this->sut->get();
+ }
+}
diff --git a/Test/Unit/Reader/FileReaderTest.php b/Test/Unit/Reader/FileReaderTest.php
new file mode 100644
index 0000000..8cc1767
--- /dev/null
+++ b/Test/Unit/Reader/FileReaderTest.php
@@ -0,0 +1,99 @@
+urlBuilderMock = $this->getMockBuilder(UrlInterface::class)->getMock();
+ $this->dirFactoryMock = $this->createMock(\FilesystemIteratorFactory::class);
+ $this->dateTimeMock = $this->getMockBuilder(DateTime::class)->getMock();
+
+ $this->dirMock = $this->createMock(\FilesystemIterator::class);
+
+ $this->dirFactoryMock->method('create')->with([
+ 'path' => DirectoryList::MEDIA . DIRECTORY_SEPARATOR . self::DESTINATION,
+ 'flags' => \FilesystemIterator::SKIP_DOTS
+ ])->willReturn($this->dirMock);
+
+ $this->sut = new FileReader($this->urlBuilderMock, $this->dirFactoryMock, $this->dateTimeMock);
+ $this->sut->setDestination(self::DESTINATION);
+ }
+
+ public function testDirDoesntExists(): void
+ {
+ $this->dirFactoryMock->method('create')->with([
+ 'path' => DirectoryList::MEDIA . DIRECTORY_SEPARATOR . self::DESTINATION,
+ 'flags' => \FilesystemIterator::SKIP_DOTS
+ ])->willThrowException(new \UnexpectedValueException);
+
+ $this->assertEquals([], $this->sut->read());
+ }
+
+ public function testReadWithoutFiles(): void
+ {
+
+ $this->dirMock->method('valid')->willReturnOnConsecutiveCalls(true, true, false);
+
+ $storeMediaUrl = 'https://local.magento2.com/media';
+ $this->urlBuilderMock->method('getBaseUrl')
+ ->with(['_type' => UrlInterface::URL_TYPE_MEDIA])
+ ->willReturn($storeMediaUrl);
+
+ $this->dirMock->method('isDir')->willReturn(true);
+
+ $this->assertEquals([], $this->sut->read());
+ }
+
+ public function testRead(): void
+ {
+
+ $this->dirMock->method('valid')->willReturnOnConsecutiveCalls(true, true, false);
+
+ $storeMediaUrl = 'https://local.magento2.com/media';
+ $this->urlBuilderMock->method('getBaseUrl')
+ ->with(['_type' => UrlInterface::URL_TYPE_MEDIA])
+ ->willReturn($storeMediaUrl);
+
+ $this->dirMock->method('isDir')->willReturn(false);
+
+ $fileGenerationTime = '2022-09-21 15:10:58';
+ $filename = 'base_store_default_feed_1.xml';
+ $filePath = self::DESTINATION;
+ $fileMTime = 1663686469;
+
+ $this->dirMock->method('getMTime')->willReturn($fileMTime);
+ $this->dateTimeMock->method('setTimestamp')
+ ->with($fileMTime)
+ ->willReturn($fileMTime);
+ $this->dateTimeMock->method('format')
+ ->with('Y-m-d H:i:s')
+ ->willReturn($fileGenerationTime);
+ $this->dirMock->method('getFilename')->willReturn($filename);
+ $this->dirMock->method('getPath')->willReturn($filePath);
+
+ $resultItem = current($this->sut->read());
+
+ $this->assertArrayHasKey('link', $resultItem);
+ $this->assertArrayHasKey('fileGenerationTime', $resultItem);
+ }
+}
diff --git a/Test/Unit/Ui/DataProvider/GoogleFeeds/ListingDataProviderTest.php b/Test/Unit/Ui/DataProvider/GoogleFeeds/ListingDataProviderTest.php
new file mode 100644
index 0000000..1a901a7
--- /dev/null
+++ b/Test/Unit/Ui/DataProvider/GoogleFeeds/ListingDataProviderTest.php
@@ -0,0 +1,66 @@
+createMock(ReportingInterface::class);
+ $searchCriteriaBuilderMock = $this->createMock(SearchCriteriaBuilder::class);
+ $requestMock = $this->createMock(RequestInterface::class);
+ $filterBuilderMock = $this->createMock(FilterBuilder::class);
+ $this->feedRepositoryMock = $this->createMock(FeedRepositoryInterface::class);
+
+ $this->sut = new ListingDataProvider(
+ $name,
+ $primaryFieldName,
+ $requestFieldName,
+ $reportingMock,
+ $searchCriteriaBuilderMock,
+ $requestMock,
+ $filterBuilderMock,
+ $this->feedRepositoryMock
+ );
+ }
+
+ public function getEmptyDataTest(): void
+ {
+ $this->feedRepositoryMock->method('getList')->willReturn([]);
+ $expected = [
+ 'items' => [],
+ 'totalRecords' => 0
+ ];
+
+ $this->assertEquals($expected, $this->sut->getData());
+ }
+
+ public function testGetDataTest()
+ {
+ $item = [
+ 'filename' => 'base_store_default_feed.xml',
+ 'path' => 'media/run_as_root/feed/base_store_default_feed.xml',
+ 'link' => 'https://local.magento2.com/media/run_as_root/feed/base_store_default_feed.xml',
+ 'last_generated' => date('Y-m-d H:i:s'),
+ 'store' => 'default'
+ ];
+
+ $this->feedRepositoryMock->method('getList')->willReturn([$item]);
+ $this->assertArrayHasKey('link', $this->sut->getData()['items'][0]);
+ }
+}
diff --git a/Test/Unit/Writer/FileWriterTest.php b/Test/Unit/Writer/FileWriterTest.php
index fb97415..e8baf78 100644
--- a/Test/Unit/Writer/FileWriterTest.php
+++ b/Test/Unit/Writer/FileWriterTest.php
@@ -37,7 +37,7 @@ public function testWrite(): void
{
$streamMock = $this->getMockBuilder(FileWriteInterface::class)->getMock();
- $destination = 'media/run_as_root/feed/base_store_seidenland_de_feed.xml';
+ $destination = 'media/run_as_root/feed/base_store_default_feed.xml';
$this->sut->setDestination($destination);
$this->mediaDirectoryMock
diff --git a/Test/Unit/Writer/XmlFileWriterProviderTest.php b/Test/Unit/Writer/XmlFileWriterProviderTest.php
index ac8f852..b4976c8 100644
--- a/Test/Unit/Writer/XmlFileWriterProviderTest.php
+++ b/Test/Unit/Writer/XmlFileWriterProviderTest.php
@@ -50,7 +50,7 @@ public function testGet1(): void
->with($storeId)
->willReturn($websiteMock);
- $storeCode = 'seidenland_de';
+ $storeCode = 'default';
$storeMock->expects($this->once())
->method('getCode')
->willReturn($storeCode);
@@ -67,7 +67,7 @@ public function testGet1(): void
$fileWriterMock->expects($this->once())
->method('setDestination')
- ->with('run_as_root/feed/base_store_seidenland_de_feed.xml')
+ ->with('run_as_root/feed/base_store_default_feed.xml')
->willReturnSelf();
$this->sut->get($storeMock);
diff --git a/Ui/DataProvider/GoogleFeeds/ListingDataProvider.php b/Ui/DataProvider/GoogleFeeds/ListingDataProvider.php
new file mode 100644
index 0000000..50faad7
--- /dev/null
+++ b/Ui/DataProvider/GoogleFeeds/ListingDataProvider.php
@@ -0,0 +1,52 @@
+feedRepository = $feedRepository;
+ }
+
+ public function getData(): array
+ {
+ $items = $this->feedRepository->getList();
+ return [
+ 'items' => $items,
+ 'totalRecords' => count($items)
+ ];
+ }
+}
\ No newline at end of file
diff --git a/etc/acl.xml b/etc/acl.xml
new file mode 100644
index 0000000..b795db6
--- /dev/null
+++ b/etc/acl.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/etc/adminhtml/menu.xml b/etc/adminhtml/menu.xml
new file mode 100644
index 0000000..59aa866
--- /dev/null
+++ b/etc/adminhtml/menu.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
diff --git a/etc/adminhtml/routes.xml b/etc/adminhtml/routes.xml
new file mode 100644
index 0000000..781acd5
--- /dev/null
+++ b/etc/adminhtml/routes.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/etc/di.xml b/etc/di.xml
index e306199..beb09cb 100644
--- a/etc/di.xml
+++ b/etc/di.xml
@@ -81,4 +81,9 @@
special_price
+
+
+
diff --git a/etc/module.xml b/etc/module.xml
index 82c3d91..36626bf 100644
--- a/etc/module.xml
+++ b/etc/module.xml
@@ -1,6 +1,6 @@
-
+
diff --git a/registration.php b/registration.php
index 10b99c6..b0ab70d 100644
--- a/registration.php
+++ b/registration.php
@@ -4,4 +4,8 @@
use Magento\Framework\Component\ComponentRegistrar;
-ComponentRegistrar::register(ComponentRegistrar::MODULE, 'RunAsRoot_Feed', __DIR__);
+ComponentRegistrar::register(
+ ComponentRegistrar::MODULE,
+ 'RunAsRoot_GoogleShoppingFeed',
+ __DIR__
+);
diff --git a/view/adminhtml/layout/run_as_root_google_feeds.xml b/view/adminhtml/layout/run_as_root_google_feeds.xml
new file mode 100644
index 0000000..41bd502
--- /dev/null
+++ b/view/adminhtml/layout/run_as_root_google_feeds.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/view/adminhtml/ui_component/google_shopping_feed_listing.xml b/view/adminhtml/ui_component/google_shopping_feed_listing.xml
new file mode 100644
index 0000000..9a89038
--- /dev/null
+++ b/view/adminhtml/ui_component/google_shopping_feed_listing.xml
@@ -0,0 +1,54 @@
+
+
+
+ -
+
- google_shopping_feed_listing.google_shopping_feed_listing_data_source
+ - google_shopping_feed_listing.google_shopping_feed_listing_data_source
+
+ - google_shopping_feed_columns
+
+
+
+ RunAsRoot\GoogleShoppingFeed\Ui\DataProvider\GoogleFeeds\ListingDataProvider
+ google_shopping_feed_listing_data_source
+ filename
+ filename
+
+ -
+
- Magento_Ui/js/grid/provider
+
+ -
+
- filename
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file