Rest: Move TransformHandler to core (part 1)
Begin moving the transform endpoints and handler class to MediaWiki core. Bug: T301370 Change-Id: I94e9d2e8d497c1992c542001afe333fa7537e553
This commit is contained in:
parent
54d1d58f4c
commit
80ce1fe28f
7 changed files with 3583 additions and 1 deletions
132
includes/Rest/Handler/ParsoidFormatHelper.php
Normal file
132
includes/Rest/Handler/ParsoidFormatHelper.php
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (C) 2011-2020 Wikimedia Foundation and others.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
namespace MediaWiki\Rest\Handler;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use MediaWiki\Rest\ResponseInterface;
|
||||
|
||||
/**
|
||||
* Format-related REST API helper.
|
||||
* Probably should be turned into an object encapsulating format and content version at some point.
|
||||
*/
|
||||
class ParsoidFormatHelper {
|
||||
|
||||
public const FORMAT_WIKITEXT = 'wikitext';
|
||||
public const FORMAT_HTML = 'html';
|
||||
public const FORMAT_PAGEBUNDLE = 'pagebundle';
|
||||
public const FORMAT_LINT = 'lint';
|
||||
|
||||
public const ERROR_ENCODING = [
|
||||
self::FORMAT_WIKITEXT => 'plain',
|
||||
self::FORMAT_HTML => 'html',
|
||||
self::FORMAT_PAGEBUNDLE => 'json',
|
||||
self::FORMAT_LINT => 'json',
|
||||
];
|
||||
|
||||
public const VALID_PAGE = [
|
||||
self::FORMAT_WIKITEXT, self::FORMAT_HTML, self::FORMAT_PAGEBUNDLE, self::FORMAT_LINT
|
||||
];
|
||||
|
||||
public const VALID_TRANSFORM = [
|
||||
self::FORMAT_WIKITEXT => [ self::FORMAT_HTML, self::FORMAT_PAGEBUNDLE, self::FORMAT_LINT ],
|
||||
self::FORMAT_HTML => [ self::FORMAT_WIKITEXT ],
|
||||
self::FORMAT_PAGEBUNDLE => [ self::FORMAT_WIKITEXT, self::FORMAT_PAGEBUNDLE ],
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the content type appropriate for a given response format.
|
||||
* @param string $format One of the FORMAT_* constants
|
||||
* @param ?string $contentVersion Output version, only for HTML and pagebundle
|
||||
* formats. See Env::getcontentVersion().
|
||||
* @return string
|
||||
*/
|
||||
public static function getContentType(
|
||||
string $format, ?string $contentVersion = null
|
||||
): string {
|
||||
if ( $format !== self::FORMAT_WIKITEXT && !$contentVersion ) {
|
||||
throw new InvalidArgumentException( '$contentVersion is required for this format' );
|
||||
}
|
||||
|
||||
switch ( $format ) {
|
||||
case self::FORMAT_WIKITEXT:
|
||||
$contentType = 'text/plain';
|
||||
// PORT-FIXME in the original the version number is from MWParserEnvironment.wikitextVersion
|
||||
// but it did not seem to be used anywhere
|
||||
$profile = 'https://www.mediawiki.org/wiki/Specs/wikitext/1.0.0';
|
||||
break;
|
||||
case self::FORMAT_HTML:
|
||||
$contentType = 'text/html';
|
||||
$profile = 'https://www.mediawiki.org/wiki/Specs/HTML/' . $contentVersion;
|
||||
break;
|
||||
case self::FORMAT_PAGEBUNDLE:
|
||||
$contentType = 'application/json';
|
||||
$profile = 'https://www.mediawiki.org/wiki/Specs/pagebundle/' . $contentVersion;
|
||||
break;
|
||||
default:
|
||||
throw new InvalidArgumentException( "Invalid format $format" );
|
||||
}
|
||||
return "$contentType; charset=utf-8; profile=\"$profile\"";
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Content-Type header appropriate for a given response format.
|
||||
* @param ResponseInterface $response
|
||||
* @param string $format One of the FORMAT_* constants
|
||||
* @param ?string $contentVersion Output version, only for HTML and pagebundle
|
||||
* formats. See Env::getcontentVersion().
|
||||
*/
|
||||
public static function setContentType(
|
||||
ResponseInterface $response, string $format,
|
||||
?string $contentVersion = null
|
||||
): void {
|
||||
$response->setHeader( 'Content-Type', self::getContentType( $format, $contentVersion ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a Content-Type header and return the format type and version.
|
||||
* Mostly the inverse of getContentType() but also accounts for legacy formats.
|
||||
* @param string $contentTypeHeader The value of the Content-Type header.
|
||||
* @param ?string &$format Format type will be set here (as a FORMAT_* constant).
|
||||
* @return ?string Format version, or null if it couldn't be identified.
|
||||
* @see Env::getInputContentVersion()
|
||||
*/
|
||||
public static function parseContentTypeHeader(
|
||||
string $contentTypeHeader, ?string &$format = null
|
||||
): ?string {
|
||||
$newProfileSyntax = 'https://www.mediawiki.org/wiki/Specs/(HTML|pagebundle)/';
|
||||
$oldProfileSyntax = 'mediawiki.org/specs/(html)/';
|
||||
$profileRegex = "#\bprofile=\"(?:$newProfileSyntax|$oldProfileSyntax)(\d+\.\d+\.\d+)\"#";
|
||||
preg_match( $profileRegex, $contentTypeHeader, $m );
|
||||
if ( $m ) {
|
||||
switch ( $m[1] ?: $m[2] ) {
|
||||
case 'HTML':
|
||||
case 'html':
|
||||
$format = self::FORMAT_HTML;
|
||||
break;
|
||||
case 'pagebundle':
|
||||
$format = self::FORMAT_PAGEBUNDLE;
|
||||
break;
|
||||
}
|
||||
return $m[3];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
1097
includes/Rest/Handler/ParsoidHandler.php
Normal file
1097
includes/Rest/Handler/ParsoidHandler.php
Normal file
File diff suppressed because it is too large
Load diff
129
includes/Rest/Handler/TransformHandler.php
Normal file
129
includes/Rest/Handler/TransformHandler.php
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Copyright (C) 2011-2020 Wikimedia Foundation and others.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
namespace MediaWiki\Rest\Handler;
|
||||
|
||||
use MediaWiki\Rest\HttpException;
|
||||
use MediaWiki\Rest\Response;
|
||||
use Wikimedia\ParamValidator\ParamValidator;
|
||||
|
||||
/**
|
||||
* Handler for transforming content given in the request.
|
||||
* - /{domain}/v1/transform/{from}/to/{format}
|
||||
* - /{domain}/v1/transform/{from}/to/{format}/{title}
|
||||
* - /{domain}/v1/transform/{from}/to/{format}/{title}/{revision}
|
||||
*
|
||||
* @see https://www.mediawiki.org/wiki/Parsoid/API#POST
|
||||
*/
|
||||
class TransformHandler extends ParsoidHandler {
|
||||
/** @inheritDoc */
|
||||
public function getParamSettings() {
|
||||
return [ // The host part of $wgServer (without the port)
|
||||
// TODO: Make this parameter optional, no needed in core.
|
||||
'domain' => [ self::PARAM_SOURCE => 'path',
|
||||
ParamValidator::PARAM_TYPE => 'string',
|
||||
ParamValidator::PARAM_REQUIRED => true, ],
|
||||
'from' => [ self::PARAM_SOURCE => 'path',
|
||||
ParamValidator::PARAM_TYPE => 'string',
|
||||
ParamValidator::PARAM_REQUIRED => true, ],
|
||||
'format' => [ self::PARAM_SOURCE => 'path',
|
||||
ParamValidator::PARAM_TYPE => 'string',
|
||||
ParamValidator::PARAM_REQUIRED => true, ],
|
||||
'title' => [ self::PARAM_SOURCE => 'path',
|
||||
ParamValidator::PARAM_TYPE => 'string',
|
||||
ParamValidator::PARAM_REQUIRED => false, ],
|
||||
'revision' => [ self::PARAM_SOURCE => 'path',
|
||||
ParamValidator::PARAM_TYPE => 'string',
|
||||
ParamValidator::PARAM_REQUIRED => false, ], ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform content given in the request from or to wikitext.
|
||||
*
|
||||
* @return Response
|
||||
* @throws HttpException
|
||||
*/
|
||||
public function execute(): Response {
|
||||
$request = $this->getRequest();
|
||||
$from = $request->getPathParam( 'from' );
|
||||
$format = $request->getPathParam( 'format' );
|
||||
if ( !isset( ParsoidFormatHelper::VALID_TRANSFORM[$from] ) || !in_array( $format,
|
||||
ParsoidFormatHelper::VALID_TRANSFORM[$from],
|
||||
true ) ) {
|
||||
throw new HttpException( "Invalid transform: ${from}/to/${format}",
|
||||
404 );
|
||||
}
|
||||
$attribs = &$this->getRequestAttributes();
|
||||
if ( !$this->acceptable( $attribs ) ) { // mutates $attribs
|
||||
throw new HttpException( 'Not acceptable',
|
||||
406 );
|
||||
}
|
||||
if ( $from === ParsoidFormatHelper::FORMAT_WIKITEXT ) {
|
||||
// Accept wikitext as a string or object{body,headers}
|
||||
$wikitext = $attribs['opts']['wikitext'] ?? null;
|
||||
if ( is_array( $wikitext ) ) {
|
||||
$wikitext = $wikitext['body'];
|
||||
// We've been given a pagelanguage for this page.
|
||||
if ( isset( $attribs['opts']['wikitext']['headers']['content-language'] ) ) {
|
||||
$attribs['pagelanguage'] = $attribs['opts']['wikitext']['headers']['content-language'];
|
||||
}
|
||||
}
|
||||
// We've been given source for this page
|
||||
if ( $wikitext === null && isset( $attribs['opts']['original']['wikitext'] ) ) {
|
||||
$wikitext = $attribs['opts']['original']['wikitext']['body'];
|
||||
// We've been given a pagelanguage for this page.
|
||||
if ( isset( $attribs['opts']['original']['wikitext']['headers']['content-language'] ) ) {
|
||||
$attribs['pagelanguage'] = $attribs['opts']['original']['wikitext']['headers']['content-language'];
|
||||
}
|
||||
}
|
||||
// Abort if no wikitext or title.
|
||||
if ( $wikitext === null && $attribs['titleMissing'] ) {
|
||||
throw new HttpException( 'No title or wikitext was provided.',
|
||||
400 );
|
||||
}
|
||||
$pageConfig = $this->tryToCreatePageConfig( $attribs,
|
||||
$wikitext );
|
||||
|
||||
return $this->wt2html( $pageConfig,
|
||||
$attribs,
|
||||
$wikitext );
|
||||
} elseif ( $format === ParsoidFormatHelper::FORMAT_WIKITEXT ) {
|
||||
$html = $attribs['opts']['html'] ?? null;
|
||||
// Accept html as a string or object{body,headers}
|
||||
if ( is_array( $html ) ) {
|
||||
$html = $html['body'];
|
||||
}
|
||||
if ( $html === null ) {
|
||||
throw new HttpException( 'No html was supplied.',
|
||||
400 );
|
||||
}
|
||||
$wikitext = $attribs['opts']['original']['wikitext']['body'] ?? null;
|
||||
$pageConfig = $this->tryToCreatePageConfig( $attribs,
|
||||
$wikitext,
|
||||
true );
|
||||
|
||||
return $this->html2wt( $pageConfig,
|
||||
$attribs,
|
||||
$html );
|
||||
} else {
|
||||
return $this->pb2pb( $attribs );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -34,5 +34,23 @@
|
|||
"UserNameUtils"
|
||||
],
|
||||
"mode": "user"
|
||||
},
|
||||
{
|
||||
"method": "POST",
|
||||
"path": "/coredev/v0/{domain}/transform/{from}/to/{format}/{title}/{revision}",
|
||||
"class": "MediaWiki\\Rest\\Handler\\TransformHandler",
|
||||
"factory": "MediaWiki\\Rest\\Handler\\TransformHandler::factory"
|
||||
},
|
||||
{
|
||||
"method": "POST",
|
||||
"path": "/coredev/v0/{domain}/transform/{from}/to/{format}",
|
||||
"class": "MediaWiki\\Rest\\Handler\\TransformHandler",
|
||||
"factory": "MediaWiki\\Rest\\Handler\\TransformHandler::factory"
|
||||
},
|
||||
{
|
||||
"method": "POST",
|
||||
"path": "/coredev/v0/{domain}/transform/{from}/to/{format}/{title}",
|
||||
"class": "MediaWiki\\Rest\\Handler\\TransformHandler",
|
||||
"factory": "MediaWiki\\Rest\\Handler\\TransformHandler::factory"
|
||||
}
|
||||
]
|
||||
|
|
|
|||
15
package-lock.json
generated
15
package-lock.json
generated
|
|
@ -12,6 +12,7 @@
|
|||
"@wdio/mocha-framework": "7.16.13",
|
||||
"@wdio/spec-reporter": "7.16.13",
|
||||
"api-testing": "1.5.0",
|
||||
"domino": "2.1.0",
|
||||
"dotenv": "8.2.0",
|
||||
"eslint-config-wikimedia": "0.22.1",
|
||||
"grunt": "1.5.2",
|
||||
|
|
@ -2782,6 +2783,12 @@
|
|||
"url": "https://github.com/fb55/domhandler?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/domino": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/domino/-/domino-2.1.0.tgz",
|
||||
"integrity": "sha512-xINSODvrnuQcm3eXJN4IkBR+JxqLrJN8Ge4fd00y1b7HsY0A4huKN5BflSS/oo8quBWmocTfWdFvrw2H8TjGqQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/domutils": {
|
||||
"version": "2.8.0",
|
||||
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
|
||||
|
|
@ -10289,7 +10296,7 @@
|
|||
}
|
||||
},
|
||||
"tests/selenium/wdio-mediawiki": {
|
||||
"version": "2.0.0",
|
||||
"version": "2.1.0",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
|
@ -12507,6 +12514,12 @@
|
|||
"domelementtype": "^2.2.0"
|
||||
}
|
||||
},
|
||||
"domino": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/domino/-/domino-2.1.0.tgz",
|
||||
"integrity": "sha512-xINSODvrnuQcm3eXJN4IkBR+JxqLrJN8Ge4fd00y1b7HsY0A4huKN5BflSS/oo8quBWmocTfWdFvrw2H8TjGqQ==",
|
||||
"dev": true
|
||||
},
|
||||
"domutils": {
|
||||
"version": "2.8.0",
|
||||
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
"@wdio/mocha-framework": "7.16.13",
|
||||
"@wdio/spec-reporter": "7.16.13",
|
||||
"api-testing": "1.5.0",
|
||||
"domino": "2.1.0",
|
||||
"dotenv": "8.2.0",
|
||||
"eslint-config-wikimedia": "0.22.1",
|
||||
"grunt": "1.5.2",
|
||||
|
|
|
|||
2192
tests/api-testing/REST/Transform.js
Normal file
2192
tests/api-testing/REST/Transform.js
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue