diff --git a/app/code/Magento/Indexer/Console/Command/IndexerStatusCommand.php b/app/code/Magento/Indexer/Console/Command/IndexerStatusCommand.php
index 1e57676b89ba8..4ea640093b67c 100644
--- a/app/code/Magento/Indexer/Console/Command/IndexerStatusCommand.php
+++ b/app/code/Magento/Indexer/Console/Command/IndexerStatusCommand.php
@@ -7,6 +7,8 @@
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
+use Magento\Framework\Indexer;
+use Magento\Framework\Mview;
/**
* Command for displaying status of indexers.
@@ -30,21 +32,84 @@ protected function configure()
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
+ $table = $this->getHelperSet()->get('table');
+ $table->setHeaders(['Title', 'Status', 'Update On', 'Schedule Status', 'Schedule Updated']);
+
+ $rows = [];
+
$indexers = $this->getIndexers($input);
foreach ($indexers as $indexer) {
- $status = 'unknown';
- switch ($indexer->getStatus()) {
- case \Magento\Framework\Indexer\StateInterface::STATUS_VALID:
- $status = 'Ready';
- break;
- case \Magento\Framework\Indexer\StateInterface::STATUS_INVALID:
- $status = 'Reindex required';
- break;
- case \Magento\Framework\Indexer\StateInterface::STATUS_WORKING:
- $status = 'Processing';
- break;
+ $view = $indexer->getView();
+
+ $rowData = [
+ 'Title' => $indexer->getTitle(),
+ 'Status' => $this->getStatus($indexer),
+ 'Update On' => $indexer->isScheduled() ? 'Schedule' : 'Save',
+ 'Schedule Status' => '',
+ 'Updated' => '',
+ ];
+
+ if ($indexer->isScheduled()) {
+ $state = $view->getState();
+ $rowData['Schedule Status'] = "{$state->getStatus()} ({$this->getPendingCount($view)} in backlog)";
+ $rowData['Updated'] = $state->getUpdated();
}
- $output->writeln(sprintf('%-50s ', $indexer->getTitle() . ':') . $status);
+
+ $rows[] = $rowData;
+ }
+
+ usort($rows, function ($comp1, $comp2) {
+ return strcmp($comp1['Title'], $comp2['Title']);
+ });
+
+ $table->addRows($rows);
+ $table->render($output);
+ }
+
+ /**
+ * @param Indexer\IndexerInterface $indexer
+ * @return string
+ */
+ private function getStatus(Indexer\IndexerInterface $indexer)
+ {
+ $status = 'unknown';
+ switch ($indexer->getStatus()) {
+ case \Magento\Framework\Indexer\StateInterface::STATUS_VALID:
+ $status = 'Ready';
+ break;
+ case \Magento\Framework\Indexer\StateInterface::STATUS_INVALID:
+ $status = 'Reindex required';
+ break;
+ case \Magento\Framework\Indexer\StateInterface::STATUS_WORKING:
+ $status = 'Processing';
+ break;
}
+ return $status;
+ }
+
+ /**
+ * @param Mview\ViewInterface $view
+ * @return string
+ */
+ private function getPendingCount(Mview\ViewInterface $view)
+ {
+ $changelog = $view->getChangelog();
+
+ try {
+ $currentVersionId = $changelog->getVersion();
+ } catch (Mview\View\ChangelogTableNotExistsException $e) {
+ return '';
+ }
+
+ $state = $view->getState();
+
+ $pendingCount = count($changelog->getList($state->getVersionId(), $currentVersionId));
+
+ $pendingString = "$pendingCount";
+ if ($pendingCount <= 0) {
+ $pendingString = "$pendingCount";
+ }
+
+ return $pendingString;
}
}
diff --git a/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerStatusCommandTest.php b/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerStatusCommandTest.php
index 5bedfb2f0223c..55d62429cd28c 100644
--- a/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerStatusCommandTest.php
+++ b/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerStatusCommandTest.php
@@ -5,9 +5,17 @@
*/
namespace Magento\Indexer\Test\Unit\Console\Command;
+use Magento\Framework\Indexer\StateInterface;
use Magento\Indexer\Console\Command\IndexerStatusCommand;
use Symfony\Component\Console\Tester\CommandTester;
+use Symfony\Component\Console\Helper\HelperSet;
+use Symfony\Component\Console\Helper\TableHelper;
+use Magento\Framework\Indexer\IndexerInterface;
+use Magento\Indexer\Model\Indexer\Collection;
+/**
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ */
class IndexerStatusCommandTest extends AbstractIndexerCommandCommonSetup
{
/**
@@ -17,46 +25,256 @@ class IndexerStatusCommandTest extends AbstractIndexerCommandCommonSetup
*/
private $command;
- public function testExecuteAll()
+ /**
+ * @var Collection|\PHPUnit_Framework_MockObject_MockObject
+ */
+ protected $indexerCollection;
+
+ public function setUp()
+ {
+ parent::setUp();
+
+ $this->indexerCollection = $this->getMockBuilder(Collection::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->collectionFactory
+ ->method('create')
+ ->willReturn($this->indexerCollection);
+ }
+
+ /**
+ * @param \PHPUnit_Framework_MockObject_MockObject $indexerMock
+ * @param array $data
+ * @return mixed
+ */
+ private function attachViewToIndexerMock($indexerMock, array $data)
+ {
+ /** @var \Magento\Framework\Mview\View\Changelog|\PHPUnit_Framework_MockObject_MockObject $changelog */
+ $changelog = $this->getMockBuilder(\Magento\Framework\Mview\View\Changelog::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $changelog->expects($this->any())
+ ->method('getList')
+ ->willReturn(range(0, $data['view']['changelog']['list_size']-1));
+
+ /** @var \Magento\Indexer\Model\Mview\View\State|\PHPUnit_Framework_MockObject_MockObject $stateMock */
+ $stateMock = $this->getMockBuilder(\Magento\Indexer\Model\Mview\View\State::class)
+ ->disableOriginalConstructor()
+ ->setMethods(null)
+ ->getMock();
+
+ $stateMock->addData($data['view']['state']);
+
+ /** @var \Magento\Framework\Mview\View|\PHPUnit_Framework_MockObject_MockObject $viewMock */
+ $viewMock = $this->getMockBuilder(\Magento\Framework\Mview\View::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['getChangelog', 'getState'])
+ ->getMock();
+
+ $viewMock->expects($this->any())
+ ->method('getState')
+ ->willReturn($stateMock);
+ $viewMock->expects($this->any())
+ ->method('getChangelog')
+ ->willReturn($changelog);
+
+ $indexerMock->method('getView')
+ ->willReturn($viewMock);
+
+ return $indexerMock;
+ }
+
+ /**
+ * @param array $indexers
+ *
+ * @dataProvider executeAllDataProvider
+ */
+ public function testExecuteAll(array $indexers)
{
$this->configureAdminArea();
- $collection = $this->getMock('Magento\Indexer\Model\Indexer\Collection', [], [], '', false);
- $indexerOne = $this->getMock('Magento\Indexer\Model\Indexer', [], [], '', false);
- $indexerOne->expects($this->once())->method('getTitle')->willReturn('Title_indexerOne');
- $indexerOne
- ->expects($this->once())
- ->method('getStatus')
- ->willReturn(\Magento\Framework\Indexer\StateInterface::STATUS_VALID);
- $indexerTwo = $this->getMock('Magento\Indexer\Model\Indexer', [], [], '', false);
- $indexerTwo->expects($this->once())->method('getTitle')->willReturn('Title_indexerTwo');
- $indexerTwo
- ->expects($this->once())
- ->method('getStatus')
- ->willReturn(\Magento\Framework\Indexer\StateInterface::STATUS_INVALID);
- $indexerThree = $this->getMock('Magento\Indexer\Model\Indexer', [], [], '', false);
- $indexerThree->expects($this->once())->method('getTitle')->willReturn('Title_indexerThree');
- $indexerThree
- ->expects($this->once())
- ->method('getStatus')
- ->willReturn(\Magento\Framework\Indexer\StateInterface::STATUS_WORKING);
- $indexerFour = $this->getMock('Magento\Indexer\Model\Indexer', [], [], '', false);
- $indexerFour->expects($this->once())->method('getTitle')->willReturn('Title_indexerFour');
- $collection
- ->expects($this->once())
- ->method('getItems')
- ->willReturn([$indexerOne, $indexerTwo, $indexerThree, $indexerFour]);
+ $indexerMocks = [];
+ foreach ($indexers as $indexerData) {
+ $indexerMock = $this->getIndexerMock(
+ ['getStatus', 'isScheduled', 'getState', 'getView'],
+ $indexerData
+ );
+
+ $indexerMock->method('getStatus')
+ ->willReturn($indexerData['status']);
+ $indexerMock->method('isScheduled')
+ ->willReturn($indexerData['is_scheduled']);
+
+ if ($indexerData['is_scheduled']) {
+ $this->attachViewToIndexerMock($indexerMock, $indexerData);
+ }
- $this->collectionFactory->expects($this->once())->method('create')->will($this->returnValue($collection));
- $this->indexerFactory->expects($this->never())->method('create');
+ $indexerMocks[] = $indexerMock;
+ }
+
+ $this->initIndexerCollectionByItems($indexerMocks);
$this->command = new IndexerStatusCommand($this->objectManagerFactory);
+
+ $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
+
+ $this->command->setHelperSet(
+ $objectManager->getObject(
+ HelperSet::class,
+ ['helpers' => [$objectManager->getObject(TableHelper::class)]]
+ )
+ );
+
$commandTester = new CommandTester($this->command);
$commandTester->execute([]);
- $actualValue = $commandTester->getDisplay();
- $expectedValue = sprintf('%-50s ', 'Title_indexerOne' . ':') . 'Ready' . PHP_EOL
- . sprintf('%-50s ', 'Title_indexerTwo' . ':') . 'Reindex required' . PHP_EOL
- . sprintf('%-50s ', 'Title_indexerThree' . ':') . 'Processing' . PHP_EOL
- . sprintf('%-50s ', 'Title_indexerFour' . ':') . 'unknown' . PHP_EOL;
- $this->assertStringStartsWith($expectedValue, $actualValue);
+ $linesOutput = array_filter(explode(PHP_EOL, $commandTester->getDisplay()));
+
+ $spacer = '+----------------+------------------+-----------+-------------------------+---------------------+';
+
+ $this->assertCount(8, $linesOutput, 'There should be 8 lines output. 3 Spacers, 1 header, 4 content.');
+ $this->assertEquals($linesOutput[0], $spacer, "Lines 0, 2, 7 should be spacer lines");
+ $this->assertEquals($linesOutput[2], $spacer, "Lines 0, 2, 7 should be spacer lines");
+ $this->assertEquals($linesOutput[7], $spacer, "Lines 0, 2, 7 should be spacer lines");
+
+ $headerValues = array_values(array_filter(explode('|', $linesOutput[1])));
+ $this->assertEquals('Title', trim($headerValues[0]));
+ $this->assertEquals('Status', trim($headerValues[1]));
+ $this->assertEquals('Update On', trim($headerValues[2]));
+ $this->assertEquals('Schedule Status', trim($headerValues[3]));
+ $this->assertEquals('Schedule Updated', trim($headerValues[4]));
+
+ $indexer1 = array_values(array_filter(explode('|', $linesOutput[3])));
+ $this->assertEquals('Title_indexer1', trim($indexer1[0]));
+ $this->assertEquals('Ready', trim($indexer1[1]));
+ $this->assertEquals('Schedule', trim($indexer1[2]));
+ $this->assertEquals('idle (10 in backlog)', trim($indexer1[3]));
+ $this->assertEquals('2017-01-01 11:11:11', trim($indexer1[4]));
+
+ $indexer2 = array_values(array_filter(explode('|', $linesOutput[4])));
+ $this->assertEquals('Title_indexer2', trim($indexer2[0]));
+ $this->assertEquals('Reindex required', trim($indexer2[1]));
+ $this->assertEquals('Save', trim($indexer2[2]));
+ $this->assertEquals('', trim($indexer2[3]));
+ $this->assertEquals('', trim($indexer2[4]));
+
+ $indexer3 = array_values(array_filter(explode('|', $linesOutput[5])));
+ $this->assertEquals('Title_indexer3', trim($indexer3[0]));
+ $this->assertEquals('Processing', trim($indexer3[1]));
+ $this->assertEquals('Schedule', trim($indexer3[2]));
+ $this->assertEquals('idle (100 in backlog)', trim($indexer3[3]));
+ $this->assertEquals('2017-01-01 11:11:11', trim($indexer3[4]));
+
+ $indexer4 = array_values(array_filter(explode('|', $linesOutput[6])));
+ $this->assertEquals('Title_indexer4', trim($indexer4[0]));
+ $this->assertEquals('unknown', trim($indexer4[1]));
+ $this->assertEquals('Schedule', trim($indexer4[2]));
+ $this->assertEquals('running (20 in backlog)', trim($indexer4[3]));
+ $this->assertEquals('2017-01-01 11:11:11', trim($indexer4[4]));
+ }
+
+ /**
+ * @return array
+ */
+ public function executeAllDataProvider()
+ {
+ return [
+ [
+ 'indexers' => [
+ 'indexer_1' => [
+ 'indexer_id' => 'indexer_1',
+ 'title' => 'Title_indexer1',
+ 'status' => StateInterface::STATUS_VALID,
+ 'is_scheduled' => true,
+ 'view' => [
+ 'state' => [
+ 'status' => 'idle',
+ 'updated' => '2017-01-01 11:11:11',
+ ],
+ 'changelog' => [
+ 'list_size' => 10
+ ]
+ ]
+ ],
+ 'indexer_2' => [
+ 'indexer_id' => 'indexer_2',
+ 'title' => 'Title_indexer2',
+ 'status' => StateInterface::STATUS_INVALID,
+ 'is_scheduled' => false,
+ 'view' => [
+ 'state' => [
+ 'status' => 'idle',
+ 'updated' => '2017-01-01 11:11:11',
+ ],
+ 'changelog' => [
+ 'list_size' => 99999999
+ ]
+ ]
+ ],
+ 'indexer_3' => [
+ 'indexer_id' => 'indexer_3',
+ 'title' => 'Title_indexer3',
+ 'status' => StateInterface::STATUS_WORKING,
+ 'is_scheduled' => true,
+ 'view' => [
+ 'state' => [
+ 'status' => 'idle',
+ 'updated' => '2017-01-01 11:11:11',
+ ],
+ 'changelog' => [
+ 'list_size' => 100
+ ]
+ ]
+ ],
+ 'indexer_4' => [
+ 'indexer_id' => 'indexer_4',
+ 'title' => 'Title_indexer4',
+ 'status' => null,
+ 'is_scheduled' => true,
+ 'view' => [
+ 'state' => [
+ 'status' => 'running',
+ 'updated' => '2017-01-01 11:11:11',
+ ],
+ 'changelog' => [
+ 'list_size' => 20
+ ]
+ ]
+ ],
+ ],
+ ],
+ ];
+ }
+
+ /**
+ * @param array $methods
+ * @param array $data
+ * @return \PHPUnit_Framework_MockObject_MockObject|IndexerInterface
+ */
+ protected function getIndexerMock(array $methods = [], array $data = [])
+ {
+ /** @var \PHPUnit_Framework_MockObject_MockObject|IndexerInterface $indexer */
+ $indexer = $this->getMockBuilder(IndexerInterface::class)
+ ->setMethods(array_merge($methods, ['getId', 'getTitle']))
+ ->getMockForAbstractClass();
+ $indexer->method('getId')
+ ->willReturn(isset($data['indexer_id']) ? $data['indexer_id'] : '');
+ $indexer->method('getTitle')
+ ->willReturn(isset($data['title']) ? $data['title'] : '');
+ return $indexer;
+ }
+
+ /**
+ * Init Indexer Collection Mock by items.
+ *
+ * @param IndexerInterface[] $items
+ * @throws \Exception
+ */
+ protected function initIndexerCollectionByItems(array $items)
+ {
+ $this->indexerCollection
+ ->method('getItems')
+ ->with()
+ ->willReturn($items);
}
}
diff --git a/app/code/Magento/ProductVideo/view/frontend/web/js/fotorama-add-video-events.js b/app/code/Magento/ProductVideo/view/frontend/web/js/fotorama-add-video-events.js
index 67bc04fb46c1f..17e38222e2c31 100644
--- a/app/code/Magento/ProductVideo/view/frontend/web/js/fotorama-add-video-events.js
+++ b/app/code/Magento/ProductVideo/view/frontend/web/js/fotorama-add-video-events.js
@@ -175,10 +175,10 @@ define([
*/
clearEvents: function () {
this.fotoramaItem.off(
- 'fotorama:show ' +
- 'fotorama:showend ' +
- 'fotorama:fullscreenenter ' +
- 'fotorama:fullscreenexit'
+ 'fotorama:show.' + this.PV +
+ ' fotorama:showend.' + this.PV +
+ ' fotorama:fullscreenenter.' + this.PV +
+ ' fotorama:fullscreenexit.' + this.PV
);
},
@@ -207,7 +207,7 @@ define([
if (options.dataMergeStrategy === 'prepend') {
this.options.videoData = [].concat(
this.options.optionsVideoData[options.selectedOption],
- this.options.videoData
+ this.defaultVideoData
);
} else {
this.options.videoData = this.options.optionsVideoData[options.selectedOption];
@@ -232,11 +232,11 @@ define([
* @private
*/
_listenForFullscreen: function () {
- this.fotoramaItem.on('fotorama:fullscreenenter', $.proxy(function () {
+ this.fotoramaItem.on('fotorama:fullscreenenter.' + this.PV, $.proxy(function () {
this.isFullscreen = true;
}, this));
- this.fotoramaItem.on('fotorama:fullscreenexit', $.proxy(function () {
+ this.fotoramaItem.on('fotorama:fullscreenexit.' + this.PV, $.proxy(function () {
this.isFullscreen = false;
this._hideVideoArrows();
}, this));
@@ -468,7 +468,7 @@ define([
t;
if (!fotorama.activeFrame.$navThumbFrame) {
- this.fotoramaItem.on('fotorama:showend', $.proxy(function (evt, fotoramaData) {
+ this.fotoramaItem.on('fotorama:showend.' + this.PV, $.proxy(function (evt, fotoramaData) {
$(fotoramaData.activeFrame.$stageFrame).removeAttr('href');
}, this));
@@ -486,7 +486,7 @@ define([
this._checkForVideo(e, fotorama, t + 1);
}
- this.fotoramaItem.on('fotorama:showend', $.proxy(function (evt, fotoramaData) {
+ this.fotoramaItem.on('fotorama:showend.' + this.PV, $.proxy(function (evt, fotoramaData) {
$(fotoramaData.activeFrame.$stageFrame).removeAttr('href');
}, this));
},
@@ -528,15 +528,15 @@ define([
* @private
*/
_attachFotoramaEvents: function () {
- this.fotoramaItem.on('fotorama:showend', $.proxy(function (e, fotorama) {
+ this.fotoramaItem.on('fotorama:showend.' + this.PV, $.proxy(function (e, fotorama) {
this._startPrepareForPlayer(e, fotorama);
}, this));
- this.fotoramaItem.on('fotorama:show', $.proxy(function (e, fotorama) {
+ this.fotoramaItem.on('fotorama:show.' + this.PV, $.proxy(function (e, fotorama) {
this._unloadVideoPlayer(fotorama.activeFrame.$stageFrame.parent(), fotorama, true);
}, this));
- this.fotoramaItem.on('fotorama:fullscreenexit', $.proxy(function (e, fotorama) {
+ this.fotoramaItem.on('fotorama:fullscreenexit.' + this.PV, $.proxy(function (e, fotorama) {
fotorama.activeFrame.$stageFrame.find('.' + this.PV).remove();
this._startPrepareForPlayer(e, fotorama);
}, this));
diff --git a/app/code/Magento/Webapi/Controller/Rest.php b/app/code/Magento/Webapi/Controller/Rest.php
index c1b84a91d97ae..6db02a0b770e7 100644
--- a/app/code/Magento/Webapi/Controller/Rest.php
+++ b/app/code/Magento/Webapi/Controller/Rest.php
@@ -280,7 +280,7 @@ protected function processSchemaRequest()
$responseBody = $this->swaggerGenerator->generate(
$requestedServices,
$this->_request->getScheme(),
- $this->_request->getHttpHost(),
+ $this->_request->getHttpHost(false),
$this->_request->getRequestUri()
);
$this->_response->setBody($responseBody)->setHeader('Content-Type', 'application/json');