rest: Add 'thumbnail' and 'description' fields to the search response
By default, core adds those fields as null to each row. provideThumbnail and provideDescription hooks are introduced. Extension should subscribe on the hooks to provide a thumbnail and description respectively. Bug: T250144 Change-Id: I42c6c8ff9d887a440174af2a21c7921573b06640
This commit is contained in:
parent
4d3fed31a4
commit
7df7b923da
9 changed files with 534 additions and 36 deletions
|
|
@ -4057,5 +4057,20 @@ $row: The database row for the revision being dumped. DEPRECATED, use $rev inste
|
|||
$text: The revision text to be dumped. DEPRECATED, use $rev instead.
|
||||
$rev: The RevisionRecord that is being dumped to XML
|
||||
|
||||
'SearchResultProvideDescription': Called by REST SearchHandler in order to allow
|
||||
extensions to fill the 'description' field in search results. Warning: this
|
||||
hook as well as SearchResultPageIdentity interface is being under development and still unstable.
|
||||
$pageIdentities: an array (string=>SearchResultPageIdentity) where key is pageId.
|
||||
&$descriptions: an output array (string=>string|null) where key is pageId and value is either
|
||||
a desciption for given page or null
|
||||
|
||||
'SearchResultProvideThumbnail': Called by REST SearchHandler in order to allow
|
||||
extensions to fill the 'thumbnail' field in rest search results. Warning: this
|
||||
hook as well as SearchResultPageIdentity interface is being under development and still unstable.
|
||||
$pageIdentities: an array (string=>SearchResultPageIdentity) where key is pageId.
|
||||
&$thumbnails: an output array (string=>SearchResultThumbnail|null) where key is pageId and
|
||||
value is either a valid SearchResultThumbnail for given page or null
|
||||
|
||||
|
||||
More hooks might be available but undocumented, you can execute
|
||||
"php maintenance/findHooks.php" to find hidden ones.
|
||||
|
|
|
|||
38
includes/Rest/Entity/SearchResultPageIdentity.php
Normal file
38
includes/Rest/Entity/SearchResultPageIdentity.php
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
namespace MediaWiki\Rest\Entity;
|
||||
|
||||
/**
|
||||
* Lightweight interface representing a page identity
|
||||
*
|
||||
* @unstable
|
||||
* @note This interface is temorary solution. It will be replaced by the one from:
|
||||
* https://phabricator.wikimedia.org/T208776
|
||||
*/
|
||||
interface SearchResultPageIdentity {
|
||||
/**
|
||||
* The numerical page ID.
|
||||
* At the moment it is equivalent to Title::getArticleID()
|
||||
* @see Title::getArticleID()
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
function getId(): int;
|
||||
|
||||
/**
|
||||
* Returns the page's namespace number.
|
||||
* At the moment it is equivalent to Title::getNamespace()
|
||||
* @see Title::getNamespace()
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
function getNamespace(): int;
|
||||
|
||||
/**
|
||||
* Get the page title in DB key form.
|
||||
* At the moment it is equivalent to Title::getDBkey()
|
||||
* @see Title::getDBkey()
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function getDBkey(): string;
|
||||
}
|
||||
46
includes/Rest/Entity/SearchResultPageIdentityValue.php
Normal file
46
includes/Rest/Entity/SearchResultPageIdentityValue.php
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
namespace MediaWiki\Rest\Entity;
|
||||
|
||||
/**
|
||||
* Lightweight value class representing a page identity
|
||||
*
|
||||
* @unstable
|
||||
* @note This class is temorary solution. It will be replaced by the one from:
|
||||
* https://phabricator.wikimedia.org/T208776
|
||||
*/
|
||||
class SearchResultPageIdentityValue implements SearchResultPageIdentity {
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $id = 0;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $dbKey = '';
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $namespace = null;
|
||||
|
||||
public function __construct( int $id, int $namespace, string $dbKey ) {
|
||||
$this->id = $id;
|
||||
$this->namespace = $namespace;
|
||||
$this->dbKey = $dbKey;
|
||||
}
|
||||
|
||||
public function getId(): int {
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getNamespace(): int {
|
||||
return $this->namespace;
|
||||
}
|
||||
|
||||
public function getDBkey(): string {
|
||||
return $this->dbKey;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -3,15 +3,18 @@
|
|||
namespace MediaWiki\Rest\Handler;
|
||||
|
||||
use Config;
|
||||
use Hooks;
|
||||
use InvalidArgumentException;
|
||||
use ISearchResultSet;
|
||||
use MediaWiki\Permissions\PermissionManager;
|
||||
use MediaWiki\Rest\Entity\SearchResultPageIdentityValue;
|
||||
use MediaWiki\Rest\Handler;
|
||||
use MediaWiki\Rest\LocalizedHttpException;
|
||||
use MediaWiki\Rest\RequestInterface;
|
||||
use MediaWiki\Rest\Response;
|
||||
use MediaWiki\Rest\ResponseFactory;
|
||||
use MediaWiki\Rest\Router;
|
||||
use MediaWiki\Search\Entity\SearchResultThumbnail;
|
||||
use RequestContext;
|
||||
use SearchEngine;
|
||||
use SearchEngineConfig;
|
||||
|
|
@ -171,10 +174,10 @@ class SearchHandler extends Handler {
|
|||
}
|
||||
|
||||
/**
|
||||
* Execute search and return results.
|
||||
* Execute search and return info about pages for further processing.
|
||||
*
|
||||
* @param SearchEngine $searchEngine
|
||||
* @return SearchResult[]
|
||||
* @return array[]
|
||||
* @throws LocalizedHttpException
|
||||
*/
|
||||
private function doSearch( $searchEngine ) {
|
||||
|
|
@ -182,7 +185,7 @@ class SearchHandler extends Handler {
|
|||
|
||||
if ( $this->mode == self::COMPLETION_MODE ) {
|
||||
$completionSearch = $searchEngine->completionSearchWithVariants( $query );
|
||||
return $this->buildOutputFromSuggestions( $completionSearch->getSuggestions() );
|
||||
return $this->buildPageInfosFromSuggestions( $completionSearch->getSuggestions() );
|
||||
} else {
|
||||
$titleSearch = $searchEngine->searchTitle( $query );
|
||||
$textSearch = $searchEngine->searchText( $query );
|
||||
|
|
@ -191,70 +194,143 @@ class SearchHandler extends Handler {
|
|||
$textSearchResults = $this->getSearchResultsOrThrow( $textSearch );
|
||||
|
||||
$mergedResults = array_merge( $titleSearchResults, $textSearchResults );
|
||||
return $this->buildOutputFromSearchResults( $mergedResults );
|
||||
return $this->buildPageInfosFromSearchResults( $mergedResults );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove duplicate pages and turn results into response json objects
|
||||
* Remove duplicate pages and turn suggestions into array with
|
||||
* information needed for further processing:
|
||||
* pageId => [ $title, $suggestion, null ]
|
||||
*
|
||||
* @param SearchSuggestion[] $suggestions
|
||||
*
|
||||
* @return array page objects
|
||||
* @return array[] of pageId => [ $title, $suggestion, null ]
|
||||
*/
|
||||
private function buildOutputFromSuggestions( array $suggestions ) {
|
||||
$pages = [];
|
||||
$foundPageIds = [];
|
||||
private function buildPageInfosFromSuggestions( array $suggestions ): array {
|
||||
$pageInfos = [];
|
||||
|
||||
foreach ( $suggestions as $sugg ) {
|
||||
$title = $sugg->getSuggestedTitle();
|
||||
if ( $title && $title->exists() ) {
|
||||
$pageID = $title->getArticleID();
|
||||
if ( !isset( $foundPageIds[$pageID] ) &&
|
||||
if ( !isset( $pageInfos[$pageID] ) &&
|
||||
$this->permissionManager->quickUserCan( 'read', $this->user, $title )
|
||||
) {
|
||||
$page = [
|
||||
'id' => $pageID,
|
||||
'key' => $title->getPrefixedDBkey(),
|
||||
'title' => $title->getPrefixedText(),
|
||||
'excerpt' => $sugg->getText() ?: null,
|
||||
];
|
||||
$pages[] = $page;
|
||||
$foundPageIds[$pageID] = true;
|
||||
$pageInfos[ $pageID ] = [ $title, $sugg, null ];
|
||||
}
|
||||
}
|
||||
}
|
||||
return $pages;
|
||||
return $pageInfos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove duplicate pages and turn results into response json objects
|
||||
* Remove duplicate pages and turn search results into array with
|
||||
* information needed for further processing:
|
||||
* pageId => [ $title, null, $result ]
|
||||
*
|
||||
* @param SearchResult[] $searchResults
|
||||
*
|
||||
* @return array page objects
|
||||
* @return array[] of pageId => [ $title, null, $result ]
|
||||
*/
|
||||
private function buildOutputFromSearchResults( array $searchResults ) {
|
||||
$pages = [];
|
||||
$foundPageIds = [];
|
||||
private function buildPageInfosFromSearchResults( array $searchResults ): array {
|
||||
$pageInfos = [];
|
||||
|
||||
foreach ( $searchResults as $result ) {
|
||||
if ( !$result->isBrokenTitle() && !$result->isMissingRevision() ) {
|
||||
$title = $result->getTitle();
|
||||
$pageID = $title->getArticleID();
|
||||
if ( !isset( $foundPageIds[$pageID] ) &&
|
||||
if ( !isset( $pageInfos[$pageID] ) &&
|
||||
$this->permissionManager->quickUserCan( 'read', $this->user, $title )
|
||||
) {
|
||||
$page = [
|
||||
'id' => $pageID,
|
||||
'key' => $title->getPrefixedDBkey(),
|
||||
'title' => $title->getPrefixedText(),
|
||||
'excerpt' => $result->getTextSnippet() ?: null,
|
||||
];
|
||||
$pages[] = $page;
|
||||
$foundPageIds[$pageID] = true;
|
||||
$pageInfos[$pageID] = [ $title, null, $result ];
|
||||
}
|
||||
}
|
||||
}
|
||||
return $pages;
|
||||
return $pageInfos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn array of page info into serializable array with common information about the page
|
||||
*
|
||||
* @param array[] $pageInfos
|
||||
*
|
||||
* @return array[] of pageId => [ $title, null, $result ]
|
||||
*/
|
||||
private function buildResultFromPageInfos( array $pageInfos ): array {
|
||||
return array_map( function ( $pageInfo ) {
|
||||
list( $title, $sugg, $result ) = $pageInfo;
|
||||
return [
|
||||
'id' => $title->getArticleID(),
|
||||
'key' => $title->getPrefixedDBkey(),
|
||||
'title' => $title->getPrefixedText(),
|
||||
'excerpt' => ( $sugg ? $sugg->getText() : $result->getTextSnippet() ) ?: null,
|
||||
];
|
||||
},
|
||||
$pageInfos );
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts SearchResultThumbnail object into serializable array
|
||||
*
|
||||
* @param SearchResultThumbnail|null $thumbnail
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
private function serializeThumbnail( ?SearchResultThumbnail $thumbnail ) : ?array {
|
||||
if ( $thumbnail == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [
|
||||
'mimetype' => $thumbnail->getMimeType(),
|
||||
'size' => $thumbnail->getSize(),
|
||||
'width' => $thumbnail->getWidth(),
|
||||
'height' => $thumbnail->getHeight(),
|
||||
'duration' => $thumbnail->getDuration(),
|
||||
'url' => $thumbnail->getUrl(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn page info into serializable array with desciption field for the page.
|
||||
*
|
||||
* The information about desciption should be provided by extension by implementing
|
||||
* 'SearchResultProvideDescription' hook. Desciption is set to null if no extensions implement
|
||||
* the hook.
|
||||
* @param array $pageIdentities
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function buildDescriptionsFromPageIdentities( array $pageIdentities ) {
|
||||
$descriptions = array_fill_keys( array_keys( $pageIdentities ), null );
|
||||
|
||||
Hooks::run( 'SearchResultProvideDescription', [ $pageIdentities, &$descriptions ] );
|
||||
|
||||
return array_map( function ( $description ) {
|
||||
return [ 'description' => $description ];
|
||||
}, $descriptions );
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn page info into serializable array with thumbnail information for the page.
|
||||
*
|
||||
* The information about thumbnail should be provided by extension by implementing
|
||||
* 'SearchResultProvideThumbnail' hook. Thumbnail is set to null if no extensions implement
|
||||
* the hook.
|
||||
*
|
||||
* @param array $pageIdentities
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function buildThumbnailsFromPageIdentities( array $pageIdentities ) {
|
||||
$thumbnails = array_fill_keys( array_keys( $pageIdentities ), null );
|
||||
|
||||
Hooks::run( 'SearchResultProvideThumbnail', [ $pageIdentities, &$thumbnails ] );
|
||||
|
||||
return array_map( function ( $thumbnail ) {
|
||||
return [ 'thumbnail' => $this->serializeThumbnail( $thumbnail ) ];
|
||||
}, $thumbnails );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -263,8 +339,23 @@ class SearchHandler extends Handler {
|
|||
*/
|
||||
public function execute() {
|
||||
$searchEngine = $this->createSearchEngine();
|
||||
$results = $this->doSearch( $searchEngine );
|
||||
$response = $this->getResponseFactory()->createJson( [ 'pages' => $results ] );
|
||||
$pageInfos = $this->doSearch( $searchEngine );
|
||||
$pageIdentities = array_map( function ( $pageInfo ) {
|
||||
list( $title ) = $pageInfo;
|
||||
return new SearchResultPageIdentityValue(
|
||||
$title->getArticleID(),
|
||||
$title->getNamespace(),
|
||||
$title->getDBkey()
|
||||
);
|
||||
}, $pageInfos );
|
||||
|
||||
$result = array_map( "array_merge",
|
||||
$this->buildResultFromPageInfos( $pageInfos ),
|
||||
$this->buildDescriptionsFromPageIdentities( $pageIdentities ),
|
||||
$this->buildThumbnailsFromPageIdentities( $pageIdentities )
|
||||
);
|
||||
|
||||
$response = $this->getResponseFactory()->createJson( [ 'pages' => $result ] );
|
||||
|
||||
if ( $this->mode === self::COMPLETION_MODE && $this->completionCacheExpiry ) {
|
||||
// Type-ahead completion matches should be cached by the client and
|
||||
|
|
|
|||
25
includes/Rest/Hook/SearchResultProvideDescriptionHook.php
Normal file
25
includes/Rest/Hook/SearchResultProvideDescriptionHook.php
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
namespace MediaWiki\Rest\Hook;
|
||||
|
||||
/**
|
||||
* Called by REST SearchHandler in order to allow extensions to fill the 'description'
|
||||
* field in search results. Warning: this hook as well as SearchResultPageIdentity interface
|
||||
* is being under development and still unstable.
|
||||
*
|
||||
* @unstable
|
||||
* @ingroup Hooks
|
||||
*/
|
||||
interface SearchResultProvideDescriptionHook {
|
||||
/**
|
||||
* This hook is called when generating search results in order to fill the 'description'
|
||||
* field in an extension.
|
||||
*
|
||||
* @since 1.35
|
||||
*
|
||||
* @param array $pageIdentities an array (string=>SearchResultPageIdentity) where key is pageId.
|
||||
* @param array &$descriptions an output array (string=>string|null) where key
|
||||
* is pageId and value is either a desciption for given page or null
|
||||
*/
|
||||
public function onSearchResultProvideDescription( array $pageIdentities, &$descriptions );
|
||||
}
|
||||
25
includes/Rest/Hook/SearchResultProvideThumbnailHook.php
Normal file
25
includes/Rest/Hook/SearchResultProvideThumbnailHook.php
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
namespace MediaWiki\Rest\Hook;
|
||||
|
||||
/**
|
||||
* Called by REST SearchHandler in order to allow extensions to fill the 'thumbnail'
|
||||
* field in rest search results. Warning: this hook as well as SearchResultPageIdentity
|
||||
* interface is being under development and still unstable.
|
||||
*
|
||||
* @unstable
|
||||
* @ingroup Hooks
|
||||
*/
|
||||
interface SearchResultProvideThumbnailHook {
|
||||
/**
|
||||
* This hook is called when generating search results in order to fill the 'thumbnail'
|
||||
* field in an extension.
|
||||
*
|
||||
* @since 1.35
|
||||
*
|
||||
* @param array $pageIdentities an array (string=>SearchResultPageIdentity) where key is pageId.
|
||||
* @param array &$thumbnails an output array (string=>SearchResultThumbnail|null) where key
|
||||
* is pageId and value is either a valid SearchResultThumbnail for given page or null
|
||||
*/
|
||||
public function onSearchResultProvideThumbnail( array $pageIdentities, &$thumbnails );
|
||||
}
|
||||
136
includes/search/Entity/SearchResultThumbnail.php
Normal file
136
includes/search/Entity/SearchResultThumbnail.php
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
<?php
|
||||
|
||||
namespace MediaWiki\Search\Entity;
|
||||
|
||||
/**
|
||||
* Class that stores information about thumbnail, e. g. url, width and height
|
||||
* @newable
|
||||
*/
|
||||
class SearchResultThumbnail {
|
||||
/**
|
||||
* Internet mime type for the representation, like "image/png" or "audio/mp3"
|
||||
* @var string
|
||||
*/
|
||||
private $mimeType;
|
||||
|
||||
/**
|
||||
* Size of the representation in bytes or null if not applicable
|
||||
* @var int|null
|
||||
*/
|
||||
private $size;
|
||||
|
||||
/**
|
||||
* Duration of the representation in seconds or null if not applicable
|
||||
* @var int|null
|
||||
*/
|
||||
private $duration;
|
||||
|
||||
/**
|
||||
* Full URL to the contents of the file
|
||||
* @var string
|
||||
*/
|
||||
private $url;
|
||||
|
||||
/**
|
||||
* Width of the representation in pixels or null if not applicable
|
||||
* @var int|null
|
||||
*/
|
||||
private $width;
|
||||
|
||||
/**
|
||||
* Height of the representation in pixels or null if not applicable
|
||||
* @var int|null
|
||||
*/
|
||||
private $height;
|
||||
|
||||
/**
|
||||
* String that represent file indentity in storage or null
|
||||
* @var string|null
|
||||
*/
|
||||
private $name;
|
||||
|
||||
/**
|
||||
* @param string $mimeType Internet mime type for the representation,
|
||||
* like "image/png" or "audio/mp3"
|
||||
* @param int|null $size Size of the representation in bytes
|
||||
* @param int|null $width Width of the representation in pixels or null if not applicable
|
||||
* @param int|null $height Height of the representation in pixels or null if not applicable
|
||||
* @param int|null $duration Duration of the representation in seconds or
|
||||
* null if not applicable
|
||||
* @param string $url full URL to the contents of the file
|
||||
* @param string|null $name full URL to the contents of the file
|
||||
*/
|
||||
public function __construct(
|
||||
string $mimeType,
|
||||
?int $size,
|
||||
?int $width,
|
||||
?int $height,
|
||||
?int $duration,
|
||||
string $url,
|
||||
?string $name
|
||||
) {
|
||||
$this->mimeType = $mimeType;
|
||||
$this->size = $size;
|
||||
$this->width = $width;
|
||||
$this->height = $height;
|
||||
$this->duration = $duration;
|
||||
$this->url = $url;
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Full URL to the contents of the file
|
||||
* @return string
|
||||
*/
|
||||
public function getUrl(): string {
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Width of the representation in pixels or null if not applicable
|
||||
* @return int|null
|
||||
*/
|
||||
public function getWidth(): ?int {
|
||||
return $this->width;
|
||||
}
|
||||
|
||||
/**
|
||||
* Height of the representation in pixels or null if not applicable
|
||||
* @return int|null
|
||||
*/
|
||||
public function getHeight(): ?int {
|
||||
return $this->height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internet mime type for the representation, like "image/png" or "audio/mp3"
|
||||
* @return string
|
||||
*/
|
||||
public function getMimeType(): string {
|
||||
return $this->mimeType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Size of the representation in bytes or null if not applicable
|
||||
* @return int|null
|
||||
*/
|
||||
public function getSize(): ?int {
|
||||
return $this->size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Duration of the representation in seconds or null if not applicable
|
||||
* @return int|null
|
||||
*/
|
||||
public function getDuration(): ?int {
|
||||
return $this->duration;
|
||||
}
|
||||
|
||||
/**
|
||||
* String that represent file indentity in storage or null
|
||||
* @return string|null
|
||||
*/
|
||||
public function getName(): ?string {
|
||||
return $this->name;
|
||||
}
|
||||
}
|
||||
|
|
@ -34,6 +34,8 @@ describe( 'Search', () => {
|
|||
assert.nestedProperty( returnPage, 'id' );
|
||||
assert.nestedProperty( returnPage, 'key' );
|
||||
assert.nestedProperty( returnPage, 'excerpt' );
|
||||
assert.nestedPropertyVal( returnPage, 'thumbnail', null );
|
||||
assert.nestedPropertyVal( returnPage, 'description', null );
|
||||
assert.include( returnPage.excerpt, `<span class='searchmatch'>${searchTerm}</span>` );
|
||||
|
||||
// full-text search should not have cache-control
|
||||
|
|
@ -51,6 +53,8 @@ describe( 'Search', () => {
|
|||
assert.nestedProperty( returnPage, 'id' );
|
||||
assert.nestedProperty( returnPage, 'key' );
|
||||
assert.nestedPropertyVal( returnPage, 'excerpt', null );
|
||||
assert.nestedPropertyVal( returnPage, 'thumbnail', null );
|
||||
assert.nestedPropertyVal( returnPage, 'description', null );
|
||||
} );
|
||||
it( 'should return a single page when there is a title and text match on the same page', async () => {
|
||||
const { body } = await client.get( `/search/page?q=${pageWithOwnTitle}` );
|
||||
|
|
@ -60,6 +64,8 @@ describe( 'Search', () => {
|
|||
assert.nestedProperty( returnPage, 'id' );
|
||||
assert.nestedProperty( returnPage, 'key' );
|
||||
assert.nestedPropertyVal( returnPage, 'title', pageWithOwnTitle );
|
||||
assert.nestedPropertyVal( returnPage, 'thumbnail', null );
|
||||
assert.nestedPropertyVal( returnPage, 'description', null );
|
||||
} );
|
||||
it( 'should return two pages when both pages match', async () => {
|
||||
const { body } = await client.get( `/search/page?q=${searchTerm2}` );
|
||||
|
|
@ -104,6 +110,8 @@ describe( 'Search', () => {
|
|||
assert.nestedProperty( returnPage, 'id' );
|
||||
assert.nestedProperty( returnPage, 'key' );
|
||||
assert.nestedProperty( returnPage, 'excerpt' );
|
||||
assert.nestedPropertyVal( returnPage, 'thumbnail', null );
|
||||
assert.nestedPropertyVal( returnPage, 'description', null );
|
||||
|
||||
// completion search should encourage caching
|
||||
assert.nestedProperty( headers, 'cache-control' );
|
||||
|
|
|
|||
|
|
@ -3,12 +3,14 @@
|
|||
namespace MediaWiki\Tests\Rest\Handler;
|
||||
|
||||
use HashConfig;
|
||||
use InvalidArgumentException;
|
||||
use Language;
|
||||
use MediaWiki\Linker\LinkTarget;
|
||||
use MediaWiki\Permissions\PermissionManager;
|
||||
use MediaWiki\Rest\Handler\SearchHandler;
|
||||
use MediaWiki\Rest\LocalizedHttpException;
|
||||
use MediaWiki\Rest\RequestData;
|
||||
use MediaWiki\Search\Entity\SearchResultThumbnail;
|
||||
use MockSearchResultSet;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use SearchEngine;
|
||||
|
|
@ -345,4 +347,116 @@ class SearchHandlerTest extends \MediaWikiUnitTestCase {
|
|||
$this->assertFalse( $handler->needsWriteAccess() );
|
||||
}
|
||||
|
||||
public function testExecute_augmentedFields() {
|
||||
$titleResults = Status::newGood( new MockSearchResultSet( [
|
||||
$this->makeMockSearchResult( 'Foo', 'one' ),
|
||||
$this->makeMockSearchResult( 'FooBar', 'three' ),
|
||||
] ) );
|
||||
$textResults = Status::newGood( new MockSearchResultSet( [
|
||||
$this->makeMockSearchResult( 'Quux', 'one' ),
|
||||
$this->makeMockSearchResult( 'Xyzzy', 'three' ),
|
||||
] ) );
|
||||
|
||||
$query = 'foo';
|
||||
$request = new RequestData( [ 'queryParams' => [ 'q' => $query ] ] );
|
||||
|
||||
$handler = $this->newHandler( $query, $titleResults, $textResults );
|
||||
$data = $this->executeHandlerAndGetBodyData( $handler, $request );
|
||||
|
||||
$this->assertArrayHasKey( 'pages', $data );
|
||||
$this->assertCount( 4, $data['pages'] );
|
||||
$this->assertArrayHasKey( 'thumbnail', $data['pages'][0] );
|
||||
$this->assertNull( $data['pages'][0][ 'thumbnail' ] );
|
||||
|
||||
$this->assertArrayHasKey( 'description', $data['pages'][0] );
|
||||
$this->assertNull( $data['pages'][0][ 'description' ] );
|
||||
}
|
||||
|
||||
public function testExecute_augmentedFieldsDescriptionAndThumbnailProvided() {
|
||||
$titleResults = Status::newGood( new MockSearchResultSet( [
|
||||
$this->makeMockSearchResult( 'Foo', 'one' ),
|
||||
] ) );
|
||||
$textResults = Status::newGood( new MockSearchResultSet( [
|
||||
$this->makeMockSearchResult( 'Quux', 'one' ),
|
||||
] ) );
|
||||
|
||||
$query = 'foo';
|
||||
$request = new RequestData( [ 'queryParams' => [ 'q' => $query ] ] );
|
||||
|
||||
$handler = $this->newHandler( $query, $titleResults, $textResults );
|
||||
$this->setTemporaryHook( 'SearchResultProvideDescription',
|
||||
function ( array $pageIdentities, array &$result ) {
|
||||
foreach ( $pageIdentities as $pageId => $pageIdentity ) {
|
||||
$result[ $pageId ] = 'Description_' . $pageIdentity->getId();
|
||||
}
|
||||
} );
|
||||
|
||||
$this->setTemporaryHook( 'SearchResultProvideThumbnail',
|
||||
function ( array $pageIdentities, array &$result ) {
|
||||
foreach ( $pageIdentities as $pageId => $pageIdentity ) {
|
||||
$result[ $pageId ] = new SearchResultThumbnail(
|
||||
'image/png',
|
||||
2250,
|
||||
100,
|
||||
125,
|
||||
500,
|
||||
'http:/example.org/url_' . $pageIdentity->getId(),
|
||||
null
|
||||
);
|
||||
}
|
||||
} );
|
||||
|
||||
$data = $this->executeHandlerAndGetBodyData( $handler, $request );
|
||||
|
||||
$this->assertArrayHasKey( 'pages', $data );
|
||||
$this->assertCount( 2, $data['pages'] );
|
||||
$this->assertArrayHasKey( 'thumbnail', $data['pages'][0] );
|
||||
|
||||
$this->assertSame( 'http:/example.org/url_1', $data['pages'][0][ 'thumbnail' ]['url'] );
|
||||
$this->assertSame( 125, $data['pages'][0][ 'thumbnail' ]['height'] );
|
||||
$this->assertSame( 100, $data['pages'][0][ 'thumbnail' ]['width'] );
|
||||
$this->assertSame( 'image/png', $data['pages'][0][ 'thumbnail' ]['mimetype'] );
|
||||
$this->assertSame( 2250, $data['pages'][0][ 'thumbnail' ]['size'] );
|
||||
$this->assertSame( 500, $data['pages'][0][ 'thumbnail' ]['duration'] );
|
||||
$this->assertArrayHasKey( 'description', $data['pages'][0] );
|
||||
$this->assertSame( 'Description_1', $data['pages'][0][ 'description' ] );
|
||||
}
|
||||
|
||||
public function testExecute_NullResults() {
|
||||
$query = 'foo';
|
||||
$request = new RequestData( [ 'queryParams' => [ 'q' => $query ] ] );
|
||||
|
||||
$handler = $this->newHandler( $query, null, null );
|
||||
$data = $this->executeHandlerAndGetBodyData( $handler, $request );
|
||||
|
||||
$this->assertArrayHasKey( 'pages', $data );
|
||||
$this->assertCount( 0, $data['pages'] );
|
||||
}
|
||||
|
||||
public function testInitWrongConfig() {
|
||||
$titleResults = Status::newGood( new MockSearchResultSet( [
|
||||
$this->makeMockSearchResult( 'Foo', 'one' ),
|
||||
$this->makeMockSearchResult( 'FooBar', 'three' ),
|
||||
] ) );
|
||||
$textResults = Status::newGood( new MockSearchResultSet( [
|
||||
$this->makeMockSearchResult( 'Quux', 'one' ),
|
||||
$this->makeMockSearchResult( 'Xyzzy', 'three' ),
|
||||
] ) );
|
||||
|
||||
$query = 'foo';
|
||||
$request = new RequestData( [ 'queryParams' => [ 'q' => $query ] ] );
|
||||
|
||||
$this->expectException( InvalidArgumentException::class );
|
||||
|
||||
$handler = $this->newHandler( $query, $titleResults, $textResults );
|
||||
$data = $this->executeHandlerAndGetBodyData(
|
||||
$handler,
|
||||
$request,
|
||||
[
|
||||
'mode' => 'SomethingWrong'
|
||||
] );
|
||||
|
||||
$this->assertArrayHasKey( 'pages', $data );
|
||||
$this->assertCount( 0, $data['pages'] );
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue