Merge "Introduce the Restbase Virtual REST Service class"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Tue, 10 Mar 2015 17:08:14 +0000 (17:08 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Tue, 10 Mar 2015 17:08:14 +0000 (17:08 +0000)
autoload.php
includes/DefaultSettings.php
includes/libs/virtualrest/ParsoidVirtualRESTService.php
includes/libs/virtualrest/RestbaseVirtualRESTService.php [new file with mode: 0644]

index c700dec..3e003f1 100644 (file)
@@ -996,6 +996,7 @@ $wgAutoloadLocalClasses = array(
        'ResourceLoaderUserOptionsModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderUserOptionsModule.php',
        'ResourceLoaderUserTokensModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderUserTokensModule.php',
        'ResourceLoaderWikiModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderWikiModule.php',
+       'RestbaseVirtualRESTService' => __DIR__ . '/includes/libs/virtualrest/RestbaseVirtualRESTService.php',
        'ResultWrapper' => __DIR__ . '/includes/db/DatabaseUtility.php',
        'RevDelArchiveItem' => __DIR__ . '/includes/revisiondelete/RevDelArchiveItem.php',
        'RevDelArchiveList' => __DIR__ . '/includes/revisiondelete/RevDelArchiveList.php',
index 8080774..52c0eab 100644 (file)
@@ -7420,6 +7420,34 @@ $wgPageLanguageUseDB = false;
  */
 $wgUseLinkNamespaceDBFields = true;
 
+/**
+ * Global configuration variable for Virtual REST Services.
+ * Parameters for different services are to be declared inside
+ * $wgVirtualRestConfig['modules'], which is to be treated as an associative
+ * array. Global parameters will be merged with service-specific ones. The
+ * result will then be passed to VirtualRESTService::__construct() in the
+ * module.
+ *
+ * Example config for Parsoid:
+ *
+ *   $wgVirtualRestConfig['modules']['parsoid'] = array(
+ *     'url' => 'http://localhost:8000',
+ *     'prefix' => 'enwiki',
+ *   );
+ *
+ * @var array
+ * @since 1.25
+ */
+$wgVirtualRestConfig = array(
+       'modules' => array(),
+       'global' => array(
+               # Timeout in seconds
+               'timeout' => 360,
+               'forwardCookies' => false,
+               'HTTPProxy' => null
+       )
+);
+
 /**
  * For really cool vim folding this needs to be at the end:
  * vim: foldmarker=@{,@} foldmethod=marker
index 769cecf..32a27f7 100644 (file)
@@ -34,13 +34,18 @@ class ParsoidVirtualRESTService extends VirtualRESTService {
         *   * body: array( 'wikitext' => ... ) or array( 'wikitext' => ..., 'body' => true/false )
         *   * $title is optional
         * @param array $params Key/value map
-        *   - URL            : Parsoid server URL
+        *   - url            : Parsoid server URL
         *   - prefix         : Parsoid prefix for this wiki
         *   - timeout        : Parsoid timeout (optional)
         *   - forwardCookies : Cookies to forward to Parsoid, or false. (optional)
         *   - HTTPProxy      : Parsoid HTTP proxy (optional)
         */
        public function __construct( array $params ) {
+               // for backwards compatibility:
+               if ( isset( $params['URL'] ) ) {
+                       $params['url'] = $params['URL'];
+                       unset( $params['URL'] );
+               }
                parent::__construct( $params );
        }
 
@@ -63,7 +68,7 @@ class ParsoidVirtualRESTService extends VirtualRESTService {
                                throw new Exception( "Request type must be either 'page' or 'transform'" );
                        }
 
-                       $req['url'] = $this->params['URL'] . '/' . urlencode( $this->params['prefix'] ) . '/';
+                       $req['url'] = $this->params['url'] . '/' . urlencode( $this->params['prefix'] ) . '/';
 
                        if ( $reqType === 'page' ) {
                                $title = $parts[3];
diff --git a/includes/libs/virtualrest/RestbaseVirtualRESTService.php b/includes/libs/virtualrest/RestbaseVirtualRESTService.php
new file mode 100644 (file)
index 0000000..8fe5b92
--- /dev/null
@@ -0,0 +1,177 @@
+<?php
+/**
+ * Virtual HTTP service client for Restbase
+ *
+ * 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.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/**
+ * Virtual REST service for Restbase
+ * @since 1.25
+ */
+class RestbaseVirtualRESTService extends VirtualRESTService {
+       /**
+        * Example requests:
+        *  GET /local/v1/page/{title}/html{/revision}
+        *  POST /local/v1/transform/html/to/wikitext{/title}{/revision}
+        *   * body: array( 'html' => ... )
+        *  POST /local/v1/transform/wikitext/to/html{/title}{/revision}
+        *   * body: array( 'wikitext' => ... ) or array( 'wikitext' => ..., 'bodyOnly' => true/false )
+        *
+        * @param array $params Key/value map
+        *   - url            : Restbase server URL
+        *   - domain         : Wiki domain to use
+        *   - timeout        : request timeout in seconds (optional)
+        *   - forwardCookies : cookies to forward to Restbase/Parsoid (as a Cookie
+        *                       header string) or false (optional)
+        *                       Note: forwardCookies will in the future be a boolean
+        *                       only, signifing request cookies should be forwarded
+        *                       to the service; the current state is due to the way
+        *                       VE handles this particular parameter
+        *   - HTTPProxy      : HTTP proxy to use (optional)
+        *   - parsoidCompat  : whether to parse URL as if they were meant for Parsoid
+        *                       boolean (optional)
+        */
+       public function __construct( array $params ) {
+               // set up defaults and merge them with the given params
+               $mparams = array_merge( array(
+                       'url' => 'http://localhost:7231',
+                       'domain' => 'localhost',
+                       'timeout' => 100,
+                       'forwardCookies' => false,
+                       'HTTPProxy' => null,
+                       'parsoidCompat' => false
+               ), $params );
+               // ensure the correct domain format
+               $mparams['domain'] = preg_replace(
+                               '/^(https?:\/\/)?([^\/:]+?)(\/|:\d+\/?)?$/',
+                               '$2',
+                               $mparams['domain']
+               );
+               parent::__construct( $mparams );
+       }
+
+       public function onRequests( array $reqs, Closure $idGenFunc ) {
+
+               if ( $this->params['parsoidCompat'] ) {
+                       return $this->onParsoidRequests( $reqs, $idGenFunc );
+               }
+
+               $result = array();
+               foreach ( $reqs as $key => $req ) {
+                       // replace /local/ with the current domain
+                       $req['url'] = preg_replace( '/^\/local\//', '/' . $this->params['domain'] . '/', $req['url'] );
+                       // and prefix it with the service URL
+                       $req['url'] = $this->params['url'] . $req['url'];
+                       // set the appropriate proxy, timeout and headers
+                       if ( $this->params['HTTPProxy'] ) {
+                               $req['proxy'] = $this->params['HTTPProxy'];
+                       }
+                       if ( $this->params['timeout'] != null ) {
+                               $req['reqTimeout'] = $this->params['timeout'];
+                       }
+                       if ( $this->params['forwardCookies'] ) {
+                               $req['headers']['Cookie'] = $this->params['forwardCookies'];
+                       }
+                       $result[$key] = $req;
+               }
+
+               return $result;
+
+       }
+
+       /**
+        * Remaps Parsoid requests to Restbase paths
+        */
+       public function onParsoidRequests( array $reqs, Closure $idGeneratorFunc ) {
+
+               $result = array();
+               foreach ( $reqs as $key => $req ) {
+                       $parts = explode( '/', $req['url'] );
+                       list(
+                               $targetWiki, // 'local'
+                               $version, // 'v1'
+                               $reqType // 'page' or 'transform'
+                       ) = $parts;
+                       if ( $targetWiki !== 'local' ) {
+                               throw new Exception( "Only 'local' target wiki is currently supported" );
+                       } elseif ( $reqType !== 'page' && $reqType !== 'transform' ) {
+                               throw new Exception( "Request type must be either 'page' or 'transform'" );
+                       }
+                       $req['url'] = $this->params['url'] . '/' . $this->params['domain'] . '/v1/' . $reqType . '/';
+                       if ( $reqType === 'page' ) {
+                               $title = $parts[3];
+                               if ( $parts[4] !== 'html' ) {
+                                       throw new Exception( "Only 'html' output format is currently supported" );
+                               }
+                               $req['url'] .= 'html/' . $title;
+                               if ( isset( $parts[5] ) ) {
+                                       $req['url'] .= '/' . $parts[5];
+                               } elseif ( isset( $req['query']['oldid'] ) && $req['query']['oldid'] ) {
+                                       $req['url'] .= '/' . $req['query']['oldid'];
+                                       unset( $req['query']['oldid'] );
+                               }
+                       } elseif ( $reqType === 'transform' ) {
+                               // from / to transform
+                               $req['url'] .= $parts[3] . '/to/' . $parts[5];
+                               // the title
+                               if ( isset( $parts[6] ) ) {
+                                       $req['url'] .= '/' . $parts[6];
+                               }
+                               // revision id
+                               if ( isset( $parts[7] ) ) {
+                                       $req['url'] .= '/' . $parts[7];
+                               } elseif ( isset( $req['body']['oldid'] ) && $req['body']['oldid'] ) {
+                                       $req['url'] .= '/' . $req['body']['oldid'];
+                                       unset( $req['body']['oldid'] );
+                               }
+                               if ( $parts[4] !== 'to' ) {
+                                       throw new Exception( "Part index 4 is not 'to'" );
+                               }
+                               if ( $parts[3] === 'html' & $parts[5] === 'wikitext' ) {
+                                       if ( !isset( $req['body']['html'] ) ) {
+                                               throw new Exception( "You must set an 'html' body key for this request" );
+                                       }
+                               } elseif ( $parts[3] == 'wikitext' && $parts[5] == 'html' ) {
+                                       if ( !isset( $req['body']['wikitext'] ) ) {
+                                               throw new Exception( "You must set a 'wikitext' body key for this request" );
+                                       }
+                                       if ( isset( $req['body']['body'] ) ) {
+                                               $req['body']['bodyOnly'] = $req['body']['body'];
+                                               unset( $req['body']['body'] );
+                                       }
+                               } else {
+                                       throw new Exception( "Transformation unsupported" );
+                               }
+                       }
+                       // set the appropriate proxy, timeout and headers
+                       if ( $this->params['HTTPProxy'] ) {
+                               $req['proxy'] = $this->params['HTTPProxy'];
+                       }
+                       if ( $this->params['timeout'] != null ) {
+                               $req['reqTimeout'] = $this->params['timeout'];
+                       }
+                       if ( $this->params['forwardCookies'] ) {
+                               $req['headers']['Cookie'] = $this->params['forwardCookies'];
+                       }
+                       $result[$key] = $req;
+               }
+
+               return $result;
+
+       }
+
+}