MediaWiki  master
ActionModuleBasedHandler.php
Go to the documentation of this file.
1 <?php
2 
3 namespace MediaWiki\Rest\Handler;
4 
5 use ApiBase;
6 use ApiMain;
7 use ApiMessage;
9 use FauxRequest;
10 use IApiMessage;
15 use RequestContext;
16 use WebResponse;
22 
28 abstract class ActionModuleBasedHandler extends Handler {
29 
33  private $apiMain = null;
34 
35  protected function getUser() {
36  return $this->getApiMain()->getUser();
37  }
38 
44  public function setApiMain( ApiMain $apiMain ) {
45  $this->apiMain = $apiMain;
46  }
47 
51  public function getApiMain() {
52  if ( $this->apiMain ) {
53  return $this->apiMain;
54  }
55 
56  $context = RequestContext::getMain();
57  $session = $context->getRequest()->getSession();
58 
59  // NOTE: This being a FauxRequest instance triggers special case behavior
60  // in ApiMain, causing ApiMain::isInternalMode() to return true. Among other things,
61  // this causes ApiMain to throw errors rather than encode them in the result data.
62  $fauxRequest = new FauxRequest( [], true, $session );
63  $fauxRequest->setSessionId( $session->getSessionId() );
64 
65  $fauxContext = new RequestContext();
66  $fauxContext->setRequest( $fauxRequest );
67  $fauxContext->setUser( $context->getUser() );
68  $fauxContext->setLanguage( $context->getLanguage() );
69 
70  $this->apiMain = new ApiMain( $fauxContext, true );
71  return $this->apiMain;
72  }
73 
81  public function overrideActionModule( string $name, string $group, ApiBase $module ) {
82  $this->getApiMain()->getModuleManager()->addModule(
83  $name,
84  $group,
85  [
86  'class' => get_class( $module ),
87  'factory' => static function () use ( $module ) {
88  return $module;
89  }
90  ]
91  );
92  }
93 
103  public function execute() {
104  $apiMain = $this->getApiMain();
105 
106  $params = $this->getActionModuleParameters();
107  $request = $apiMain->getRequest();
108 
109  foreach ( $params as $key => $value ) {
110  $request->setVal( $key, $value );
111  }
112 
113  try {
114  // NOTE: ApiMain detects this to be an internal call, so it will throw
115  // ApiUsageException rather than putting error messages into the result.
116  $apiMain->execute();
117  } catch ( ApiUsageException $ex ) {
118  // use a fake loop to throw the first error
119  foreach ( $ex->getStatusValue()->getErrorsByType( 'error' ) as $error ) {
120  $msg = ApiMessage::create( $error );
121  $this->throwHttpExceptionForActionModuleError( $msg, $ex->getCode() ?: 400 );
122  }
123 
124  // This should never happen, since ApiUsageExceptions should always
125  // have errors in their Status object.
126  throw new HttpException(
127  'Unmapped action module error: ' . $ex->getMessage(),
128  $ex->getCode()
129  );
130  }
131 
132  $actionModuleResult = $apiMain->getResult()->getResultData( null, [ 'Strip' => 'all' ] );
133 
134  // construct result
135  $resultData = $this->mapActionModuleResult( $actionModuleResult );
136 
137  $response = $this->getResponseFactory()->createFromReturnValue( $resultData );
138 
140  $apiMain->getRequest()->response(),
141  $actionModuleResult,
142  $response
143  );
144 
145  return $response;
146  }
147 
157  abstract protected function getActionModuleParameters();
158 
167  abstract protected function mapActionModuleResult( array $data );
168 
185  protected function mapActionModuleResponse(
186  WebResponse $actionModuleResponse,
187  array $actionModuleResult,
188  Response $response
189  ) {
190  // TODO: map status, headers, cookies, etc
191  }
192 
211  protected function throwHttpExceptionForActionModuleError( IApiMessage $msg, $statusCode = 400 ) {
212  // override to supply mappings
213 
214  throw new LocalizedHttpException(
215  $this->makeMessageValue( $msg ),
216  $statusCode,
217  // Include the original error code in the response.
218  // This makes it easier to track down the original cause of the error,
219  // and allows more specific mappings to be added to
220  // implementations of throwHttpExceptionForActionModuleError() provided by
221  // subclasses
222  [ 'actionModuleErrorCode' => $msg->getApiCode() ]
223  );
224  }
225 
233  protected function makeMessageValue( IApiMessage $msg ) {
234  $params = [];
235 
236  // TODO: find a better home for the parameter mapping logic
237  foreach ( $msg->getParams() as $p ) {
238  $params[] = $this->makeMessageParam( $p );
239  }
240 
241  return new MessageValue( $msg->getKey(), $params );
242  }
243 
249  private function makeMessageParam( $param ) {
250  if ( is_array( $param ) ) {
251  foreach ( $param as $type => $value ) {
252  if ( $type === 'list' ) {
253  $paramList = [];
254 
255  foreach ( $value as $v ) {
256  $paramList[] = $this->makeMessageParam( $v );
257  }
258 
259  return new ListParam( ParamType::TEXT, $paramList );
260  } else {
261  return new ScalarParam( $type, $value );
262  }
263  }
264  } else {
265  return new ScalarParam( ParamType::TEXT, $param );
266  }
267  }
268 
269 }
This abstract class implements many basic API functions, and is the base of all API classes.
Definition: ApiBase.php:56
This is the main API class, used for both external and internal processing.
Definition: ApiMain.php:52
getResult()
Get the ApiResult object associated with current request.
Definition: ApiMain.php:644
execute()
Execute api request.
Definition: ApiMain.php:842
Extension of Message implementing IApiMessage.
Definition: ApiMessage.php:27
static create( $msg, $code=null, array $data=null)
Create an IApiMessage for the message.
Definition: ApiMessage.php:43
Exception used to abort API execution with an error.
getStatusValue()
Fetch the error status.
WebRequest clone which takes values from a provided array.
Definition: FauxRequest.php:37
Base class for REST handlers that are implemented by mapping to an existing ApiModule.
setApiMain(ApiMain $apiMain)
Set main action API entry point for testing.
overrideActionModule(string $name, string $group, ApiBase $module)
Overrides an action API module.
throwHttpExceptionForActionModuleError(IApiMessage $msg, $statusCode=400)
Throws a HttpException for a given IApiMessage that represents an error.
mapActionModuleResponse(WebResponse $actionModuleResponse, array $actionModuleResult, Response $response)
Transfers relevant information, such as header values, from the WebResponse constructed by the action...
execute()
Main execution method, implemented to delegate execution to ApiMain.
makeMessageValue(IApiMessage $msg)
Constructs a MessageValue from an IApiMessage.
mapActionModuleResult(array $data)
Maps an action API result to a REST API result.
getActionModuleParameters()
Maps a REST API request to an action API request.
Base class for REST route handlers.
Definition: Handler.php:20
getResponseFactory()
Get the ResponseFactory which can be used to generate Response objects.
Definition: Handler.php:179
This is the base exception class for non-fatal exceptions thrown from REST handlers.
Group all the pieces relevant to the context of a request into one instance.
static getMain()
Get the RequestContext object associated with the main request.
Allow programs to request this object from WebRequest::response() and handle all outputting (or lack ...
Definition: WebResponse.php:32
Value object representing a message parameter that consists of a list of values.
Definition: ListParam.php:12
Value object representing a message parameter that consists of a list of values.
Value object representing a message for i18n.
The constants used to specify parameter types.
Definition: ParamType.php:11
Value object representing a message parameter holding a single value.
Definition: ScalarParam.php:14
Interface for messages with machine-readable data for use by the API.
Definition: IApiMessage.php:39
getApiCode()
Returns a machine-readable code for use by the API.
getParams()
Returns the message parameters.
getKey()
Returns the message key.
Copyright (C) 2011-2020 Wikimedia Foundation and others.