repo/rest-api/src/RouteHandlers/GetItemRouteHandler.php
<?php declare( strict_types=1 );
namespace Wikibase\Repo\RestApi\RouteHandlers;
use MediaWiki\MediaWikiServices;
use MediaWiki\Rest\Handler;
use MediaWiki\Rest\RequestInterface;
use MediaWiki\Rest\Response;
use MediaWiki\Rest\ResponseInterface;
use MediaWiki\Rest\SimpleHandler;
use MediaWiki\Rest\StringStream;
use Wikibase\Repo\RestApi\Application\Serialization\AliasesSerializer;
use Wikibase\Repo\RestApi\Application\Serialization\DescriptionsSerializer;
use Wikibase\Repo\RestApi\Application\Serialization\ItemPartsSerializer;
use Wikibase\Repo\RestApi\Application\Serialization\LabelsSerializer;
use Wikibase\Repo\RestApi\Application\Serialization\SitelinkSerializer;
use Wikibase\Repo\RestApi\Application\Serialization\SitelinksSerializer;
use Wikibase\Repo\RestApi\Application\Serialization\StatementListSerializer;
use Wikibase\Repo\RestApi\Application\UseCases\GetItem\GetItem;
use Wikibase\Repo\RestApi\Application\UseCases\GetItem\GetItemRequest;
use Wikibase\Repo\RestApi\Application\UseCases\GetItem\GetItemResponse;
use Wikibase\Repo\RestApi\Application\UseCases\ItemRedirect;
use Wikibase\Repo\RestApi\Application\UseCases\UseCaseError;
use Wikibase\Repo\RestApi\Domain\ReadModel\ItemParts;
use Wikibase\Repo\RestApi\RouteHandlers\Middleware\AuthenticationMiddleware;
use Wikibase\Repo\RestApi\RouteHandlers\Middleware\MiddlewareHandler;
use Wikibase\Repo\RestApi\RouteHandlers\Middleware\UserAgentCheckMiddleware;
use Wikibase\Repo\RestApi\WbRestApi;
use Wikimedia\ParamValidator\ParamValidator;
/**
* @license GPL-2.0-or-later
*/
class GetItemRouteHandler extends SimpleHandler {
public const ROUTE = '/wikibase/v0/entities/items/{item_id}';
public const ITEM_ID_PATH_PARAM = 'item_id';
private const FIELDS_QUERY_PARAM = '_fields';
private GetItem $getItem;
private ItemPartsSerializer $itemPartsSerializer;
private ResponseFactory $responseFactory;
private MiddlewareHandler $middlewareHandler;
public function __construct(
GetItem $getItem,
ItemPartsSerializer $itemPartsSerializer,
ResponseFactory $responseFactory,
MiddlewareHandler $middlewareHandler
) {
$this->getItem = $getItem;
$this->itemPartsSerializer = $itemPartsSerializer;
$this->responseFactory = $responseFactory;
$this->middlewareHandler = $middlewareHandler;
}
public static function factory(): Handler {
$responseFactory = new ResponseFactory();
return new self(
WbRestApi::getGetItem(),
new ItemPartsSerializer(
new LabelsSerializer(),
new DescriptionsSerializer(),
new AliasesSerializer(),
new StatementListSerializer( WbRestApi::getStatementSerializer() ),
new SitelinksSerializer( new SitelinkSerializer() )
),
$responseFactory,
new MiddlewareHandler( [
WbRestApi::getUnexpectedErrorHandlerMiddleware(),
new UserAgentCheckMiddleware(),
new AuthenticationMiddleware( MediaWikiServices::getInstance()->getUserIdentityUtils() ),
WbRestApi::getPreconditionMiddlewareFactory()->newPreconditionMiddleware(
fn( RequestInterface $request ): string => $request->getPathParam( self::ITEM_ID_PATH_PARAM )
),
] )
);
}
/**
* @param mixed ...$args
*/
public function run( ...$args ): Response {
return $this->middlewareHandler->run( $this, [ $this, 'runUseCase' ], $args );
}
public function runUseCase( string $id ): Response {
$fields = explode( ',', $this->getValidatedParams()[self::FIELDS_QUERY_PARAM] );
try {
return $this->newSuccessHttpResponse(
$this->getItem->execute( new GetItemRequest( $id, $fields ) )
);
} catch ( ItemRedirect $e ) {
return $this->newRedirectHttpResponse( $e );
} catch ( UseCaseError $e ) {
return $this->responseFactory->newErrorResponseFromException( $e );
}
}
private function newSuccessHttpResponse( GetItemResponse $useCaseResponse ): Response {
$httpResponse = $this->getResponseFactory()->create();
$httpResponse->setHeader( 'Content-Type', 'application/json' );
$httpResponse->setHeader( 'Last-Modified', wfTimestamp( TS_RFC2822, $useCaseResponse->getLastModified() ) );
$this->setEtagFromRevId( $httpResponse, $useCaseResponse->getRevisionId() );
$httpResponse->setBody( new StringStream(
json_encode( $this->itemPartsSerializer->serialize( $useCaseResponse->getItemParts() ), JSON_UNESCAPED_SLASHES )
) );
return $httpResponse;
}
private function newRedirectHttpResponse( ItemRedirect $e ): Response {
$httpResponse = $this->getResponseFactory()->create();
$httpResponse->setHeader(
'Location',
$this->getRouteUrl(
[ self::ITEM_ID_PATH_PARAM => $e->getRedirectTargetId() ],
$this->getRequest()->getQueryParams()
)
);
$httpResponse->setStatus( 308 );
return $httpResponse;
}
private function setEtagFromRevId( Response $response, int $revId ): void {
$response->setHeader( 'ETag', "\"$revId\"" );
}
public function getParamSettings(): array {
return [
self::ITEM_ID_PATH_PARAM => [
self::PARAM_SOURCE => 'path',
ParamValidator::PARAM_TYPE => 'string',
ParamValidator::PARAM_REQUIRED => true,
],
self::FIELDS_QUERY_PARAM => [
self::PARAM_SOURCE => 'query',
ParamValidator::PARAM_TYPE => 'string',
ParamValidator::PARAM_REQUIRED => false,
ParamValidator::PARAM_ISMULTI => false,
ParamValidator::PARAM_DEFAULT => implode( ',', ItemParts::VALID_FIELDS ),
],
];
}
public function needsWriteAccess(): bool {
return false;
}
/**
* Preconditions are checked via {@link PreconditionMiddleware}
*/
public function checkPreconditions(): ?ResponseInterface {
return null;
}
}