View source
<?php
namespace Drupal\jsonrpc;
use Drupal\Component\Plugin\Exception\PluginException;
use Drupal\Component\Plugin\PluginManagerInterface;
use Drupal\Core\Access\AccessResultReasonInterface;
use Drupal\Core\Render\AttachmentsInterface;
use Drupal\Core\Render\RenderContext;
use Drupal\Core\Render\RendererInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\jsonrpc\Exception\JsonRpcException;
use Drupal\jsonrpc\Object\Error;
use Drupal\jsonrpc\Object\ParameterBag;
use Drupal\jsonrpc\Object\Request;
use Drupal\jsonrpc\Object\Response;
class Handler implements HandlerInterface {
const SUPPORTED_VERSION = '2.0';
protected $methodManager;
protected $renderer;
public function __construct(PluginManagerInterface $method_manager, RendererInterface $renderer) {
$this->methodManager = $method_manager;
$this->renderer = $renderer;
}
public static function supportedVersion() {
return static::SUPPORTED_VERSION;
}
public function batch(array $requests) {
return array_filter(array_map(function (Request $request) {
return $this
->doRequest($request);
}, $requests));
}
public function supportedMethods() {
return $this->methodManager
->getDefinitions();
}
public function supportsMethod($name) {
return !is_null($this
->getMethod($name));
}
public function availableMethods(AccountInterface $account = NULL) {
return array_filter($this
->supportedMethods(), function (MethodInterface $method) {
return $method
->access('execute');
});
}
public function getMethod($name) {
return $this->methodManager
->getDefinition($name, FALSE);
}
protected function doRequest(Request $request) {
$handle_exception = function ($e, Request $request) {
if (!$e instanceof JsonRpcException) {
$id = $request
->isNotification() ? FALSE : $request
->id();
$e = JsonRpcException::fromPrevious($e, $id);
}
return $e
->getResponse();
};
try {
$context = new RenderContext();
$result = $this->renderer
->executeInRenderContext($context, function () use ($request) {
return $this
->doExecution($request);
});
if ($request
->isNotification()) {
return NULL;
}
$rpc_response = $result instanceof Response ? $result : new Response(static::SUPPORTED_VERSION, $request
->id(), $result);
$methodPluginClass = $this
->getMethod($request
->getMethod())
->getClass();
$result_schema = call_user_func([
$methodPluginClass,
'outputSchema',
]);
$rpc_response
->setResultSchema($result_schema);
$response_headers = $this
->getMethod($request
->getMethod())->responseHeaders;
$rpc_response
->getHeaders()
->add($response_headers);
if (!$context
->isEmpty()) {
$bubbleable_metadata = $context
->pop();
$rpc_response
->addCacheableDependency($bubbleable_metadata);
if ($rpc_response instanceof AttachmentsInterface) {
$rpc_response
->addAttachments($bubbleable_metadata
->getAttachments());
}
}
return $rpc_response;
} catch (\Throwable $e) {
return $handle_exception($e, $request);
} catch (\Exception $e) {
return $handle_exception($e, $request);
}
}
protected function doExecution(Request $request) {
if ($method = $this
->getMethod($request
->getMethod())) {
$this
->checkAccess($method);
$configuration = [
HandlerInterface::JSONRPC_REQUEST_KEY => $request,
];
$executable = $this
->getExecutable($method, $configuration);
return $request
->hasParams() ? $executable
->execute($request
->getParams()) : $executable
->execute(new ParameterBag([]));
}
else {
throw JsonRpcException::fromError(Error::methodNotFound($method
->id()));
}
}
protected function getExecutable(MethodInterface $method, array $configuration) {
try {
return $this->methodManager
->createInstance($method
->id(), $configuration);
} catch (PluginException $e) {
throw JsonRpcException::fromError(Error::methodNotFound($method
->id()));
}
}
protected function checkAccess(MethodInterface $method) {
$access_result = $method
->access('execute', NULL, TRUE);
if (!$access_result
->isAllowed()) {
$reason = 'Access Denied';
if ($access_result instanceof AccessResultReasonInterface && ($detail = $access_result
->getReason())) {
$reason .= ': ' . $detail;
}
throw JsonRpcException::fromError(Error::invalidRequest($reason, $access_result));
}
}
}