diff --git a/modules/helfi_react_search/helfi_react_search.module b/modules/helfi_react_search/helfi_react_search.module index 1d5df6ca1..2079621f7 100644 --- a/modules/helfi_react_search/helfi_react_search.module +++ b/modules/helfi_react_search/helfi_react_search.module @@ -37,3 +37,19 @@ function helfi_react_search_preprocess_paragraph(array &$variables) : void { $variables['#attached']['drupalSettings']['helfi_react_search']['elastic_proxy_url'] = $proxyUrl; } } + +/** + * Implements hook_theme(). + */ +function helfi_react_search_theme() : array { + return [ + 'debug_item__search_api' => [ + 'variables' => [ + 'id' => NULL, + 'label' => NULL, + 'data' => [], + ], + 'template' => 'debug-item--search-api', + ], + ]; +} diff --git a/modules/helfi_react_search/src/Plugin/DebugDataItem/SearchApiIndex.php b/modules/helfi_react_search/src/Plugin/DebugDataItem/SearchApiIndex.php new file mode 100644 index 000000000..b6ff0ea9d --- /dev/null +++ b/modules/helfi_react_search/src/Plugin/DebugDataItem/SearchApiIndex.php @@ -0,0 +1,106 @@ +entityTypeManager = $container->get('entity_type.manager'); + + return $instance; + } + + /** + * {@inheritdoc} + */ + public function collect(): array { + $data = []; + + if (!$this->entityTypeManager->hasDefinition('search_api_index')) { + return []; + } + $indexes = $this->entityTypeManager + ->getStorage('search_api_index') + ->loadMultiple(); + + if (!$indexes) { + return []; + } + /** @var \Drupal\search_api\IndexInterface $index */ + foreach ($indexes as $index) { + $result = $status = NULL; + + try { + $status = $index->getServerInstance()?->isAvailable(); + $tracker = $index->getTrackerInstance(); + + $result = $this->resolveResult( + $tracker->getIndexedItemsCount(), + $tracker->getTotalItemsCount() + ); + + } + catch (SearchApiException) { + } + $data[] = [ + 'id' => $index->getOriginalId(), + 'result' => $result, + 'status' => $status, + ]; + } + + return $data; + } + + /** + * Resolve return value based on index status. + * + * @param int $indexed + * Amount of up-to-date items in index. + * @param int $total + * Maximum number of items in index. + * + * @return string + * Status. + */ + private function resolveResult(int $indexed, int $total): string { + if ($indexed == 0 || $total == 0) { + return 'indexing or index rebuild required'; + } + + if ($indexed === $total) { + return 'Index up to date'; + } + + return "$indexed/$total"; + } + +} diff --git a/modules/helfi_react_search/templates/debug-item--search-api.html.twig b/modules/helfi_react_search/templates/debug-item--search-api.html.twig new file mode 100644 index 000000000..d3fa9fccf --- /dev/null +++ b/modules/helfi_react_search/templates/debug-item--search-api.html.twig @@ -0,0 +1,45 @@ +{# +/** + * To actually show any data from a custom plugin, you *MUST* override this template + * with a template called 'debug-item--{{ id }}.html.twig'. + * + * For example: debug-item--composer.html.twig, where 'composer' is your plugin's ID. + * + * You can then loop your data with something like this: + * {% for item in data.packages %} + * {{ item.name }} + * {{ item.version }} + * {% endfor %} + * + * Available variables: + * - id: The ID of your plugin + * - label The label of your plugin + * - data: An array of data returned by your plugin's collect() method. + */ +#} + +

{{ 'Search API indexes'|t }}

+ + + + + + + + + + {% for value in data %} + + + + + + {% endfor %} + +
IndexStatusResult
+ {{ value.id }} + + {{ value.status ? 'Online'|t : 'Offline'|t }} + + {{ value.result }} +
diff --git a/modules/helfi_react_search/tests/src/Unit/SearchApiIndexItemTest.php b/modules/helfi_react_search/tests/src/Unit/SearchApiIndexItemTest.php new file mode 100644 index 000000000..3db830ffa --- /dev/null +++ b/modules/helfi_react_search/tests/src/Unit/SearchApiIndexItemTest.php @@ -0,0 +1,139 @@ +prophesize(EntityTypeManagerInterface::class); + $entityTypeManager->hasDefinition('search_api_index')->willReturn(FALSE); + $container = new ContainerBuilder(); + $container->set('entity_type.manager', $entityTypeManager->reveal()); + $sut = SearchApiIndex::create($container, [], '', []); + $this->assertEmpty($sut->collect()); + } + + /** + * @covers ::create + * @covers ::collect + */ + public function testNoIndex() : void { + $indexStorage = $this->prophesize(SearchApiConfigEntityStorage::class); + $indexStorage->loadMultiple()->willReturn([]); + $entityTypeManager = $this->prophesize(EntityTypeManagerInterface::class); + $entityTypeManager->hasDefinition('search_api_index') + ->willReturn(TRUE); + $entityTypeManager->getStorage('search_api_index') + ->willReturn($indexStorage->reveal()); + $container = new ContainerBuilder(); + $container->set('entity_type.manager', $entityTypeManager->reveal()); + + $sut = SearchApiIndex::create($container, [], '', []); + $this->assertEmpty($sut->collect()); + } + + /** + * @covers ::create + * @covers ::collect + * @covers ::resolveResult + */ + public function testCollect() : void { + $index1 = $this->prophesize(IndexInterface::class); + $index1->getOriginalId()->willReturn('index1'); + $index1->getServerInstance()->willThrow(new SearchApiException()); + + $server = $this->prophesize(ServerInterface::class); + $server->isAvailable()->willReturn(TRUE); + + $tracker2 = $this->prophesize(TrackerInterface::class); + $tracker2->getIndexedItemsCount()->willReturn(0); + $tracker2->getTotalItemsCount()->willReturn(0); + + $index2 = $this->prophesize(IndexInterface::class); + $index2->getOriginalId()->willReturn('index2'); + $index2->getServerInstance()->willReturn($server->reveal()); + $index2->getTrackerInstance()->willReturn($tracker2->reveal()); + + $tracker3 = $this->prophesize(TrackerInterface::class); + $tracker3->getIndexedItemsCount()->willReturn(20); + $tracker3->getTotalItemsCount()->willReturn(20); + + $index3 = $this->prophesize(IndexInterface::class); + $index3->getOriginalId()->willReturn('index3'); + $index3->getServerInstance()->willReturn($server->reveal()); + $index3->getTrackerInstance()->willReturn($tracker3->reveal()); + + $tracker4 = $this->prophesize(TrackerInterface::class); + $tracker4->getIndexedItemsCount()->willReturn(10); + $tracker4->getTotalItemsCount()->willReturn(20); + $index4 = $this->prophesize(IndexInterface::class); + $index4->getOriginalId()->willReturn('index4'); + $index4->getServerInstance()->willReturn($server->reveal()); + $index4->getTrackerInstance()->willReturn($tracker4->reveal()); + + $indexStorage = $this->prophesize(SearchApiConfigEntityStorage::class); + $indexStorage->loadMultiple()->willReturn([ + $index1->reveal(), + $index2->reveal(), + $index3->reveal(), + $index4->reveal(), + ]); + $entityTypeManager = $this->prophesize(EntityTypeManagerInterface::class); + $entityTypeManager->getStorage('search_api_index') + ->willReturn($indexStorage->reveal()); + $entityTypeManager->hasDefinition('search_api_index') + ->willReturn(TRUE); + $container = new ContainerBuilder(); + $container->set('entity_type.manager', $entityTypeManager->reveal()); + + $sut = SearchApiIndex::create($container, [], '', []); + $this->assertEquals([ + [ + 'id' => 'index1', + 'result' => NULL, + 'status' => NULL, + ], + [ + 'id' => 'index2', + 'result' => 'indexing or index rebuild required', + 'status' => TRUE, + ], + [ + 'id' => 'index3', + 'result' => 'Index up to date', + 'status' => TRUE, + ], + [ + 'id' => 'index4', + 'result' => '10/20', + 'status' => TRUE, + ], + ], $sut->collect()); + } + +}