View source
<?php
namespace Drupal\jsonrpc\Shaper;
use Drupal\Component\Serialization\Json;
use Drupal\jsonrpc\Exception\JsonRpcException;
use Drupal\jsonrpc\HandlerInterface;
use Drupal\jsonrpc\Object\Error;
use Drupal\jsonrpc\Object\ParameterBag;
use Drupal\jsonrpc\Object\Request;
use Drupal\jsonrpc\ParameterFactory\RawParameterFactory;
use Drupal\jsonrpc\ParameterDefinitionInterface;
use JsonSchema\Constraints\Constraint;
use JsonSchema\Validator;
use Shaper\Transformation\TransformationBase;
use Shaper\Util\Context;
use Shaper\Validator\CollectionOfValidators;
use Shaper\Validator\InstanceofValidator;
use Shaper\Validator\JsonSchemaValidator;
use Symfony\Component\DependencyInjection\ContainerInterface;
class RpcRequestFactory extends TransformationBase {
const REQUEST_ID_KEY = 'jsonrpc_request_id';
const REQUEST_VERSION_KEY = 'jsonrpc_request_version';
const REQUEST_IS_BATCH_REQUEST = 'jsonrpc_request_is_batch_request';
protected $handler;
protected $container;
protected $validator;
public function __construct(HandlerInterface $handler, ContainerInterface $container, Validator $validator) {
$this->handler = $handler;
$this->container = $container;
$this->validator = $validator;
}
public function getInputValidator() {
$schema = Json::decode(file_get_contents(__DIR__ . '/request-schema.json'));
return new JsonSchemaValidator($schema, $this->validator, Constraint::CHECK_MODE_TYPE_CAST);
}
public function getOutputValidator() {
return new CollectionOfValidators(new InstanceofValidator(Request::class));
}
protected function doTransform($data, Context $context) {
$context[static::REQUEST_IS_BATCH_REQUEST] = $this
->isBatchRequest($data);
$data = $this
->isBatchRequest($data) ? $data : [
$data,
];
return array_map(function ($item) use ($context) {
return $this
->denormalizeRequest($item, $context);
}, $data);
}
protected function denormalizeRequest($data, Context $context) {
$id = isset($data['id']) ? $data['id'] : FALSE;
$context[static::REQUEST_ID_KEY] = $id;
$context[static::REQUEST_VERSION_KEY] = $this->handler
->supportedVersion();
$batch = $context[static::REQUEST_IS_BATCH_REQUEST];
$params = $this
->denormalizeParams($data, $context);
return new Request($data['jsonrpc'], $data['method'], $batch, $id, $params);
}
protected function denormalizeParams($data, Context $context) {
if (!$this->handler
->supportsMethod($data['method'])) {
throw $this
->newException(Error::methodNotFound($data['method']), $context);
}
$method = $this->handler
->getMethod($data['method']);
$params = $method
->getParams();
if (is_null($params)) {
if (isset($data['params'])) {
$error = Error::invalidParams("The {$data['method']} method does not accept parameters.");
throw $this
->newException($error, $context);
}
return NULL;
}
$arguments = [];
$positional = $method
->areParamsPositional();
foreach ($params as $key => $param) {
if (isset($data['params'][$key])) {
$arguments[$key] = $this
->denormalizeParam($data['params'][$key], $param);
}
elseif ($param
->isRequired()) {
throw $this
->newException(Error::invalidParams("Missing required parameter: {$key}"), $context);
}
}
return new ParameterBag($arguments, $positional);
}
protected function denormalizeParam($argument, ParameterDefinitionInterface $parameter_definition) {
$factory_class = $parameter_definition
->getFactory() ?: RawParameterFactory::class;
$factory = call_user_func_array([
$factory_class,
'create',
], [
$parameter_definition,
$this->container,
]);
$context = new Context([
ParameterDefinitionInterface::class => $parameter_definition,
]);
try {
return $factory
->transform($argument, $context);
} catch (\TypeError $exception) {
$message = "The {$parameter_definition->getId()} parameter does not conform to the parameter schema. {$exception->getMessage()}";
throw JsonRpcException::fromError(Error::invalidParams($message));
}
}
protected function isBatchRequest(array $data) {
if (isset($data['jsonrpc'])) {
return FALSE;
}
$supported_version = $this->handler
->supportedVersion();
$filter = function ($version) use ($supported_version) {
return $version === $supported_version;
};
if (count(array_filter(array_column($data, 'jsonrpc'), $filter)) === count($data)) {
return TRUE;
}
throw JsonRpcException::fromError(Error::invalidRequest("Every request must include a 'jsonrpc' member with a value of {$supported_version}."));
}
protected function newException(Error $error, Context $context) {
return JsonRpcException::fromError($error, $context[static::REQUEST_ID_KEY], $context[static::REQUEST_VERSION_KEY]);
}
}