From b2ec265d4fda4f012c71d8c8f6ec52732a551779 Mon Sep 17 00:00:00 2001 From: Vladimir Razuvaev Date: Wed, 19 Jul 2017 23:35:22 +0700 Subject: [PATCH] Server: ability to execute PSR7 request --- src/Server/Helper.php | 93 +++++++++++++++++++++++++---------- src/Server/StandardServer.php | 17 +++++++ 2 files changed, 84 insertions(+), 26 deletions(-) diff --git a/src/Server/Helper.php b/src/Server/Helper.php index 1508496..4e5ef4c 100644 --- a/src/Server/Helper.php +++ b/src/Server/Helper.php @@ -14,6 +14,7 @@ use GraphQL\Language\Parser; use GraphQL\Utils\AST; use GraphQL\Utils\Utils; use GraphQL\Validator\DocumentValidator; +use Psr\Http\Message\ServerRequestInterface; /** * Class Helper @@ -146,12 +147,8 @@ class Helper * @throws Error * @throws InvariantViolation */ - public function loadPersistedQuery(ServerConfig $config, OperationParams $op) + private function loadPersistedQuery(ServerConfig $config, OperationParams $op) { - if (!$op->queryId) { - throw new InvariantViolation("Could not load persisted query: queryId is not set"); - } - // Load query if we got persisted query id: $loader = $config->getPersistentQueryLoader(); @@ -177,7 +174,7 @@ class Helper * @param OperationParams $params * @return array */ - public function resolveValidationRules(ServerConfig $config, OperationParams $params) + private function resolveValidationRules(ServerConfig $config, OperationParams $params) { // Allow customizing validation rules per operation: $validationRules = $config->getValidationRules(); @@ -197,8 +194,8 @@ class Helper } /** - * Parses HTTP request and returns GraphQL QueryParams contained in this request. - * For batched requests it returns an array of QueryParams. + * Parses HTTP request and returns GraphQL OperationParams contained in this request. + * For batched requests it returns an array of OperationParams. * * This function doesn't check validity of these params. * @@ -211,44 +208,88 @@ class Helper public function parseHttpRequest(callable $readRawBodyFn = null) { $method = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : null; + $bodyParams = []; + $urlParams = $_GET; - if ($method === 'GET') { - $result = OperationParams::create($_GET, true); - } else if ($method === 'POST') { + if ($method === 'POST') { $contentType = isset($_SERVER['CONTENT_TYPE']) ? $_SERVER['CONTENT_TYPE'] : null; if (stripos($contentType, 'application/graphql') !== false) { $rawBody = $readRawBodyFn ? $readRawBodyFn() : $this->readRawBody(); - $result = OperationParams::create(['query' => $rawBody ?: '']); + $bodyParams = ['query' => $rawBody ?: '']; } else if (stripos($contentType, 'application/json') !== false) { $rawBody = $readRawBodyFn ? $readRawBodyFn() : $this->readRawBody(); - $body = json_decode($rawBody ?: '', true); + $bodyParams = json_decode($rawBody ?: '', true); if (json_last_error()) { throw new Error("Could not parse JSON: " . json_last_error_msg()); } - if (!is_array($body)) { + if (!is_array($bodyParams)) { throw new Error( "GraphQL Server expects JSON object or array, but got " . - Utils::printSafeJson($body) + Utils::printSafeJson($bodyParams) ); } - if (isset($body[0])) { - $result = []; - foreach ($body as $index => $entry) { - $op = OperationParams::create($entry); - $result[] = $op; - } - } else { - $result = OperationParams::create($body); - } } else if (stripos($contentType, 'application/x-www-form-urlencoded') !== false) { - $result = OperationParams::create($_POST); + $bodyParams = $_POST; } else if (null === $contentType) { throw new Error('Missing "Content-Type" header'); } else { throw new Error("Unexpected content type: " . Utils::printSafeJson($contentType)); } + } + + return $this->parseRequestParams($method, $bodyParams, $urlParams); + } + + /** + * Converts PSR7 request to OperationParams[] + * + * @param ServerRequestInterface $request + * @return array|Helper + */ + public function parsePsrRequest(ServerRequestInterface $request) + { + $contentType = $request->getHeader('content-type'); + if (isset($contentType[0]) && $contentType[0] === 'application/graphql') { + $bodyParams = ['query' => $request->getBody()->getContents()]; + } else { + $bodyParams = $request->getParsedBody(); + } + + return $this->parseRequestParams( + $request->getMethod(), + $bodyParams, + $request->getQueryParams() + ); + } + + /** + * Parses normalized request params and returns instance of OperationParams or array of OperationParams in + * case of batch operation. + * + * Returned value is a suitable input for `executeOperation` or `executeBatch` (if array) + * + * @param string $method + * @param array $bodyParams + * @param array $queryParams + * @return OperationParams|OperationParams[] + * @throws Error + */ + public function parseRequestParams($method, array $bodyParams, array $queryParams) + { + if ($method === 'GET') { + $result = OperationParams::create($queryParams, true); + } else if ($method === 'POST') { + if (isset($bodyParams[0])) { + $result = []; + foreach ($bodyParams as $index => $entry) { + $op = OperationParams::create($entry); + $result[] = $op; + } + } else { + $result = OperationParams::create($bodyParams); + } } else { throw new Error('HTTP Method "' . $method . '" is not supported'); } @@ -258,7 +299,7 @@ class Helper /** * @return bool|string */ - public function readRawBody() + private function readRawBody() { return file_get_contents('php://input'); } diff --git a/src/Server/StandardServer.php b/src/Server/StandardServer.php index a6f4acc..9b9d60d 100644 --- a/src/Server/StandardServer.php +++ b/src/Server/StandardServer.php @@ -4,6 +4,7 @@ namespace GraphQL\Server; use GraphQL\Error\InvariantViolation; use GraphQL\Executor\ExecutionResult; use GraphQL\Executor\Promise\Promise; +use Psr\Http\Message\ServerRequestInterface; /** * Class StandardServer @@ -47,6 +48,22 @@ class StandardServer } /** + * Executes GraphQL operation with given server configuration and returns execution result + * (or promise when promise adapter is different from SyncPromiseAdapter) + * + * @param ServerRequestInterface $request + * @return ExecutionResult|ExecutionResult[]|Promise + */ + public function executePsrRequest(ServerRequestInterface $request) + { + $parsedBody = $this->helper->parsePsrRequest($request); + return $this->executeRequest($parsedBody); + } + + /** + * Executes GraphQL operation with given server configuration and returns execution result + * (or promise when promise adapter is different from SyncPromiseAdapter) + * * @param OperationParams|OperationParams[] $parsedBody * @return ExecutionResult|ExecutionResult[]|Promise * @throws InvariantViolation