Skip to content

change operation to OperationName in the parser #3568

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
* GraphQL: Add page-based pagination (#3175, #3517)
* GraphQL: Possibility to add a custom description for queries, mutations and subscriptions (#3477, #3514)
* GraphQL: Support for field name conversion (serialized name) (#3455, #3516)
* GraphQL: **BC** `operation` is now `operationName` to follow the standard (#3568)
* OpenAPI: Add PHP default values to the documentation (#2386)
* Deprecate using a validation groups generator service not implementing `ApiPlatform\Core\Bridge\Symfony\Validator\ValidationGroupsGeneratorInterface` (#3346)

Expand All @@ -34,7 +35,6 @@
* HTTP: Location header is only set on POST with a 201 or between 300 and 400 #3497
* GraphQL: Do not allow empty cursor values on `before` or `after` #3360
* Bump versions of Swagger UI, GraphiQL and GraphQL Playground #3510
>>>>>>> 2.5

## 2.5.4

Expand Down
6 changes: 3 additions & 3 deletions features/bootstrap/GraphqlContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,11 @@ public function ISendTheGraphqlRequestWithVariables(PyStringNode $variables)
}

/**
* @When I send the GraphQL request with operation :operation
* @When I send the GraphQL request with operationName :operationName
*/
public function ISendTheGraphqlRequestWithOperation(string $operation)
public function ISendTheGraphqlRequestWithOperation(string $operationName)
{
$this->graphqlRequest['operation'] = $operation;
$this->graphqlRequest['operationName'] = $operationName;
$this->sendGraphqlRequest();
}

Expand Down
4 changes: 2 additions & 2 deletions features/graphql/query.feature
Original file line number Diff line number Diff line change
Expand Up @@ -102,13 +102,13 @@ Feature: GraphQL query support
}
}
"""
And I send the GraphQL request with operation "DummyWithId2"
And I send the GraphQL request with operationName "DummyWithId2"
Then the response status code should be 200
And the response should be in JSON
And the header "Content-Type" should be equal to "application/json"
And the JSON node "data.dummyItem.id" should be equal to "/dummies/2"
And the JSON node "data.dummyItem.name" should be equal to "Dummy #2"
And I send the GraphQL request with operation "DummyWithId1"
And I send the GraphQL request with operationName "DummyWithId1"
And the JSON node "data.dummyItem.name" should be equal to "Dummy #1"

Scenario: Use serialization groups
Expand Down
29 changes: 14 additions & 15 deletions src/GraphQl/Action/EntrypointAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,13 @@ public function __invoke(Request $request): Response
}
}

[$query, $operation, $variables] = $this->parseRequest($request);
[$query, $operationName, $variables] = $this->parseRequest($request);
if (null === $query) {
throw new BadRequestHttpException('GraphQL query is not valid.');
}

$executionResult = $this->executor
->executeQuery($this->schemaBuilder->getSchema(), $query, null, null, $variables, $operation)
->executeQuery($this->schemaBuilder->getSchema(), $query, null, null, $variables, $operationName)
->setErrorFormatter([$this->normalizer, 'normalize']);
} catch (\Exception $exception) {
$executionResult = (new ExecutionResult(null, [new Error($exception->getMessage(), null, null, null, null, $exception)]))
Expand All @@ -91,34 +91,34 @@ public function __invoke(Request $request): Response
private function parseRequest(Request $request): array
{
$query = $request->query->get('query');
$operation = $request->query->get('operation');
$operationName = $request->query->get('operationName');
if ($variables = $request->query->get('variables', [])) {
$variables = $this->decodeVariables($variables);
}

if (!$request->isMethod('POST')) {
return [$query, $operation, $variables];
return [$query, $operationName, $variables];
}

if ('json' === $request->getContentType()) {
return $this->parseData($query, $operation, $variables, $request->getContent());
return $this->parseData($query, $operationName, $variables, $request->getContent());
}

if ('graphql' === $request->getContentType()) {
$query = $request->getContent();
}

if ('multipart' === $request->getContentType()) {
return $this->parseMultipartRequest($query, $operation, $variables, $request->request->all(), $request->files->all());
return $this->parseMultipartRequest($query, $operationName, $variables, $request->request->all(), $request->files->all());
}

return [$query, $operation, $variables];
return [$query, $operationName, $variables];
}

/**
* @throws BadRequestHttpException
*/
private function parseData(?string $query, ?string $operation, array $variables, string $jsonContent): array
private function parseData(?string $query, ?string $operationName, array $variables, string $jsonContent): array
{
if (!\is_array($data = json_decode($jsonContent, true))) {
throw new BadRequestHttpException('GraphQL data is not valid JSON.');
Expand All @@ -132,24 +132,23 @@ private function parseData(?string $query, ?string $operation, array $variables,
$variables = \is_array($data['variables']) ? $data['variables'] : $this->decodeVariables($data['variables']);
}

if (isset($data['operation'])) {
$operation = $data['operation'];
if (isset($data['operationName'])) {
$operationName = $data['operationName'];
}

return [$query, $operation, $variables];
return [$query, $operationName, $variables];
}

/**
* @throws BadRequestHttpException
*/
private function parseMultipartRequest(?string $query, ?string $operation, array $variables, array $bodyParameters, array $files): array
private function parseMultipartRequest(?string $query, ?string $operationName, array $variables, array $bodyParameters, array $files): array
{
if ((null === $operations = $bodyParameters['operations'] ?? null) || (null === $map = $bodyParameters['map'] ?? null)) {
throw new BadRequestHttpException('GraphQL multipart request does not respect the specification.');
}

/** @var string $operations */
[$query, $operation, $variables] = $this->parseData($query, $operation, $variables, $operations);
[$query, $operationName, $variables] = $this->parseData($query, $operationName, $variables, $operations);

/** @var string $map */
if (!\is_array($decodedMap = json_decode($map, true))) {
Expand All @@ -158,7 +157,7 @@ private function parseMultipartRequest(?string $query, ?string $operation, array

$variables = $this->applyMapToVariables($decodedMap, $variables, $files);

return [$query, $operation, $variables];
return [$query, $operationName, $variables];
}

/**
Expand Down
22 changes: 11 additions & 11 deletions tests/GraphQl/Action/EntrypointActionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public function testGetHtmlAction(): void

public function testGetAction(): void
{
$request = new Request(['query' => 'graphqlQuery', 'variables' => '["graphqlVariable"]', 'operation' => 'graphqlOperationName']);
$request = new Request(['query' => 'graphqlQuery', 'variables' => '["graphqlVariable"]', 'operationName' => 'graphqlOperationName']);
$request->setRequestFormat('json');
$mockedEntrypoint = $this->getEntrypointAction();

Expand All @@ -67,7 +67,7 @@ public function testGetAction(): void

public function testPostRawAction(): void
{
$request = new Request(['variables' => '["graphqlVariable"]', 'operation' => 'graphqlOperationName'], [], [], [], [], [], 'graphqlQuery');
$request = new Request(['variables' => '["graphqlVariable"]', 'operationName' => 'graphqlOperationName'], [], [], [], [], [], 'graphqlQuery');
$request->setFormat('graphql', 'application/graphql');
$request->setMethod('POST');
$request->headers->set('Content-Type', 'application/graphql');
Expand All @@ -78,7 +78,7 @@ public function testPostRawAction(): void

public function testPostJsonAction(): void
{
$request = new Request([], [], [], [], [], [], '{"query": "graphqlQuery", "variables": "[\"graphqlVariable\"]", "operation": "graphqlOperationName"}');
$request = new Request([], [], [], [], [], [], '{"query": "graphqlQuery", "variables": "[\"graphqlVariable\"]", "operationName": "graphqlOperationName"}');
$request->setMethod('POST');
$request->headers->set('Content-Type', 'application/json');
$mockedEntrypoint = $this->getEntrypointAction();
Expand Down Expand Up @@ -119,14 +119,14 @@ public function multipartRequestProvider(): array

return [
'upload a single file' => [
'{"query": "graphqlQuery", "variables": {"file": null}, "operation": "graphqlOperationName"}',
'{"query": "graphqlQuery", "variables": {"file": null}, "operationName": "graphqlOperationName"}',
'{"file": ["variables.file"]}',
['file' => $file],
['file' => $file],
new JsonResponse(['GraphQL']),
],
'upload multiple files' => [
'{"query": "graphqlQuery", "variables": {"files": [null, null, null]}, "operation": "graphqlOperationName"}',
'{"query": "graphqlQuery", "variables": {"files": [null, null, null]}, "operationName": "graphqlOperationName"}',
'{"0": ["variables.files.0"], "1": ["variables.files.1"], "2": ["variables.files.2"]}',
[
'0' => $file,
Expand All @@ -150,7 +150,7 @@ public function multipartRequestProvider(): array
new Response('{"errors":[{"message":"GraphQL multipart request does not respect the specification.","extensions":{"category":"user","status":400}}]}'),
],
'upload without providing map' => [
'{"query": "graphqlQuery", "variables": {"file": null}, "operation": "graphqlOperationName"}',
'{"query": "graphqlQuery", "variables": {"file": null}, "operationName": "graphqlOperationName"}',
null,
['file' => $file],
['file' => null],
Expand All @@ -164,28 +164,28 @@ public function multipartRequestProvider(): array
new Response('{"errors":[{"message":"GraphQL data is not valid JSON.","extensions":{"category":"user","status":400}}]}'),
],
'upload with invalid map JSON' => [
'{"query": "graphqlQuery", "variables": {"file": null}, "operation": "graphqlOperationName"}',
'{"query": "graphqlQuery", "variables": {"file": null}, "operationName": "graphqlOperationName"}',
'{invalid}',
['file' => $file],
['file' => null],
new Response('{"errors":[{"message":"GraphQL multipart request map is not valid JSON.","extensions":{"category":"user","status":400}}]}'),
],
'upload with no file' => [
'{"query": "graphqlQuery", "variables": {"file": null}, "operation": "graphqlOperationName"}',
'{"query": "graphqlQuery", "variables": {"file": null}, "operationName": "graphqlOperationName"}',
'{"file": ["file"]}',
[],
['file' => null],
new Response('{"errors":[{"message":"GraphQL multipart request file has not been sent correctly.","extensions":{"category":"user","status":400}}]}'),
],
'upload with wrong map' => [
'{"query": "graphqlQuery", "variables": {"file": null}, "operation": "graphqlOperationName"}',
'{"query": "graphqlQuery", "variables": {"file": null}, "operationName": "graphqlOperationName"}',
'{"file": ["file"]}',
['file' => $file],
['file' => null],
new Response('{"errors":[{"message":"GraphQL multipart request path in map is invalid.","extensions":{"category":"user","status":400}}]}'),
],
'upload when variable path does not exist' => [
'{"query": "graphqlQuery", "variables": {"file": null}, "operation": "graphqlOperationName"}',
'{"query": "graphqlQuery", "variables": {"file": null}, "operationName": "graphqlOperationName"}',
'{"file": ["variables.wrong"]}',
['file' => $file],
['file' => null],
Expand Down Expand Up @@ -217,7 +217,7 @@ public function testBadMethodAction(): void

public function testBadVariablesAction(): void
{
$request = new Request(['query' => 'graphqlQuery', 'variables' => 'graphqlVariable', 'operation' => 'graphqlOperationName']);
$request = new Request(['query' => 'graphqlQuery', 'variables' => 'graphqlVariable', 'operationName' => 'graphqlOperationName']);
$request->setRequestFormat('json');
$mockedEntrypoint = $this->getEntrypointAction();

Expand Down