repo/rest-api/src/RouteHandlers/PatchItemAliasesRouteHandler.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 MediaWiki\Rest\Validator\BodyValidator;
use Wikibase\Repo\RestApi\Application\Serialization\AliasesSerializer;
use Wikibase\Repo\RestApi\Application\UseCases\ItemRedirect;
use Wikibase\Repo\RestApi\Application\UseCases\PatchItemAliases\PatchItemAliases;
use Wikibase\Repo\RestApi\Application\UseCases\PatchItemAliases\PatchItemAliasesRequest;
use Wikibase\Repo\RestApi\Application\UseCases\PatchItemAliases\PatchItemAliasesResponse;
use Wikibase\Repo\RestApi\Application\UseCases\UseCaseError;
use Wikibase\Repo\RestApi\RouteHandlers\Middleware\AuthenticationMiddleware;
use Wikibase\Repo\RestApi\RouteHandlers\Middleware\BotRightCheckMiddleware;
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 PatchItemAliasesRouteHandler extends SimpleHandler {
use AssertContentType;
public const ITEM_ID_PATH_PARAM = 'item_id';
public const PATCH_BODY_PARAM = 'patch';
public const TAGS_BODY_PARAM = 'tags';
public const BOT_BODY_PARAM = 'bot';
public const COMMENT_BODY_PARAM = 'comment';
private MiddlewareHandler $middlewareHandler;
private PatchItemAliases $useCase;
private AliasesSerializer $serializer;
private ResponseFactory $responseFactory;
public function __construct(
MiddlewareHandler $middlewareHandler,
PatchItemAliases $useCase,
AliasesSerializer $serializer,
ResponseFactory $responseFactory
) {
$this->middlewareHandler = $middlewareHandler;
$this->useCase = $useCase;
$this->serializer = $serializer;
$this->responseFactory = $responseFactory;
}
public static function factory(): Handler {
$responseFactory = new ResponseFactory();
return new self(
new MiddlewareHandler( [
WbRestApi::getUnexpectedErrorHandlerMiddleware(),
new UserAgentCheckMiddleware(),
new AuthenticationMiddleware(),
new BotRightCheckMiddleware( MediaWikiServices::getInstance()->getPermissionManager(), $responseFactory ),
WbRestApi::getPreconditionMiddlewareFactory()->newPreconditionMiddleware(
fn( RequestInterface $request ): string => $request->getPathParam( self::ITEM_ID_PATH_PARAM )
),
] ),
WbRestApi::getPatchItemAliases(),
new AliasesSerializer(),
$responseFactory
);
}
/**
* @param mixed ...$args
*/
public function run( ...$args ): Response {
return $this->middlewareHandler->run( $this, [ $this, 'runUseCase' ], $args );
}
public function runUseCase( string $itemId ): Response {
$jsonBody = $this->getValidatedBody();
'@phan-var array $jsonBody'; // guaranteed to be an array per getBodyValidator()
try {
return $this->newSuccessHttpResponse(
$this->useCase->execute(
new PatchItemAliasesRequest(
$itemId,
$jsonBody[ self::PATCH_BODY_PARAM ],
$jsonBody[ self::TAGS_BODY_PARAM ],
$jsonBody[ self::BOT_BODY_PARAM ],
$jsonBody[ self::COMMENT_BODY_PARAM ],
$this->getUsername()
)
)
);
} catch ( ItemRedirect $e ) {
return $this->responseFactory->newErrorResponse(
UseCaseError::ITEM_REDIRECTED,
"Item $itemId has been merged into {$e->getRedirectTargetId()}."
);
} catch ( UseCaseError $e ) {
return $this->responseFactory->newErrorResponseFromException( $e );
}
}
private function newSuccessHttpResponse( PatchItemAliasesResponse $useCaseResponse ): Response {
$httpResponse = $this->getResponseFactory()->create();
$httpResponse->setStatus( 200 );
$httpResponse->setHeader( 'Content-Type', 'application/json' );
$httpResponse->setHeader( 'ETag', "\"{$useCaseResponse->getRevisionId()}\"" );
$httpResponse->setHeader( 'Last-Modified', wfTimestamp( TS_RFC2822, $useCaseResponse->getLastModified() ) );
$httpResponse->setBody(
new StringStream( json_encode( $this->serializer->serialize( $useCaseResponse->getAliases() ) ) )
);
return $httpResponse;
}
public function getParamSettings(): array {
return [
self::ITEM_ID_PATH_PARAM => [
self::PARAM_SOURCE => 'path',
ParamValidator::PARAM_TYPE => 'string',
ParamValidator::PARAM_REQUIRED => true,
],
];
}
/**
* @inheritDoc
*/
public function getBodyValidator( $contentType ): BodyValidator {
$this->assertContentType( [ 'application/json', 'application/json-patch+json' ], $contentType );
return new TypeValidatingJsonBodyValidator( [
self::PATCH_BODY_PARAM => [
self::PARAM_SOURCE => 'body',
ParamValidator::PARAM_TYPE => 'array',
ParamValidator::PARAM_REQUIRED => true,
],
self::TAGS_BODY_PARAM => [
self::PARAM_SOURCE => 'body',
ParamValidator::PARAM_TYPE => 'array',
ParamValidator::PARAM_REQUIRED => false,
ParamValidator::PARAM_DEFAULT => [],
],
self::BOT_BODY_PARAM => [
self::PARAM_SOURCE => 'body',
ParamValidator::PARAM_TYPE => 'boolean',
ParamValidator::PARAM_REQUIRED => false,
ParamValidator::PARAM_DEFAULT => false,
],
self::COMMENT_BODY_PARAM => [
self::PARAM_SOURCE => 'body',
ParamValidator::PARAM_TYPE => 'string',
ParamValidator::PARAM_REQUIRED => false,
ParamValidator::PARAM_DEFAULT => null,
],
] );
}
/**
* Preconditions are checked via {@link PreconditionMiddleware}
*/
public function checkPreconditions(): ?ResponseInterface {
return null;
}
private function getUsername(): ?string {
$mwUser = $this->getAuthority()->getUser();
return $mwUser->isRegistered() ? $mwUser->getName() : null;
}
}