Skip to content

Commit 7b651ed

Browse files
committed
Merge branch 'ACP2E-3467' of https://github.com/adobe-commerce-tier-4/magento2ce into PR-VK-2024-12-17-CE
2 parents 248f9ed + bf06dfa commit 7b651ed

File tree

2 files changed

+176
-44
lines changed

2 files changed

+176
-44
lines changed

app/code/Magento/GraphQl/Controller/GraphQl.php

Lines changed: 55 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
<?php
2-
32
/**
4-
* Copyright © Magento, Inc. All rights reserved.
5-
* See COPYING.txt for license details.
3+
* Copyright 2017 Adobe
4+
* All Rights Reserved.
65
*/
7-
86
declare(strict_types=1);
97

108
namespace Magento\GraphQl\Controller;
119

10+
use GraphQL\Error\FormattedError;
11+
use GraphQL\Error\SyntaxError;
1212
use Magento\Framework\App\Area;
1313
use Magento\Framework\App\AreaList;
1414
use Magento\Framework\App\FrontControllerInterface;
@@ -19,6 +19,7 @@
1919
use Magento\Framework\App\ResponseInterface;
2020
use Magento\Framework\Controller\Result\JsonFactory;
2121
use Magento\Framework\GraphQl\Exception\ExceptionFormatter;
22+
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
2223
use Magento\Framework\GraphQl\Query\Fields as QueryFields;
2324
use Magento\Framework\GraphQl\Query\QueryParser;
2425
use Magento\Framework\GraphQl\Query\QueryProcessor;
@@ -183,36 +184,49 @@ public function dispatch(RequestInterface $request): ResponseInterface
183184

184185
$statusCode = 200;
185186
$jsonResult = $this->jsonFactory->create();
186-
$data = $this->getDataFromRequest($request);
187-
$result = [];
188-
187+
$data = [];
188+
$result = null;
189189
$schema = null;
190-
$query = $data['query'] ?? '';
190+
$query = '';
191+
191192
try {
193+
$data = $this->getDataFromRequest($request);
194+
$query = $data['query'] ?? '';
195+
192196
/** @var Http $request */
193197
$this->requestProcessor->validateRequest($request);
194-
$parsedQuery = $this->queryParser->parse($query);
195-
$data['parsedQuery'] = $parsedQuery;
196-
197-
// We must extract queried field names to avoid instantiation of unnecessary fields in webonyx schema
198-
// Temporal coupling is required for performance optimization
199-
$this->queryFields->setQuery($parsedQuery, $data['variables'] ?? null);
200-
$schema = $this->schemaGenerator->generate();
201-
202-
$result = $this->queryProcessor->process(
203-
$schema,
204-
$parsedQuery,
205-
$this->contextFactory->create(),
206-
$data['variables'] ?? []
207-
);
198+
if ($request->isGet() || $request->isPost()) {
199+
$parsedQuery = $this->queryParser->parse($query);
200+
$data['parsedQuery'] = $parsedQuery;
201+
202+
// We must extract queried field names to avoid instantiation of unnecessary fields in webonyx schema
203+
// Temporal coupling is required for performance optimization
204+
$this->queryFields->setQuery($parsedQuery, $data['variables'] ?? null);
205+
$schema = $this->schemaGenerator->generate();
206+
207+
$result = $this->queryProcessor->process(
208+
$schema,
209+
$parsedQuery,
210+
$this->contextFactory->create(),
211+
$data['variables'] ?? []
212+
);
213+
}
214+
} catch (SyntaxError|GraphQlInputException $error) {
215+
$result = [
216+
'errors' => [FormattedError::createFromException($error)],
217+
];
218+
$statusCode = 400;
208219
} catch (\Exception $error) {
209-
$result['errors'] = isset($result['errors']) ? $result['errors'] : [];
210-
$result['errors'][] = $this->graphQlError->create($error);
220+
$result = [
221+
'errors' => [$this->graphQlError->create($error)],
222+
];
211223
$statusCode = ExceptionFormatter::HTTP_GRAPH_QL_SCHEMA_ERROR_STATUS;
212224
}
213225

214226
$jsonResult->setHttpResponseCode($statusCode);
215-
$jsonResult->setData($result);
227+
if ($result !== null) {
228+
$jsonResult->setData($result);
229+
}
216230
$jsonResult->renderResult($this->httpResponse);
217231

218232
// log information about the query, unless it is an introspection query
@@ -229,20 +243,25 @@ public function dispatch(RequestInterface $request): ResponseInterface
229243
*
230244
* @param RequestInterface $request
231245
* @return array
246+
* @throws GraphQlInputException
232247
*/
233248
private function getDataFromRequest(RequestInterface $request): array
234249
{
235-
/** @var Http $request */
236-
if ($request->isPost()) {
237-
$data = $this->jsonSerializer->unserialize($request->getContent());
238-
} elseif ($request->isGet()) {
239-
$data = $request->getParams();
240-
$data['variables'] = isset($data['variables']) ?
241-
$this->jsonSerializer->unserialize($data['variables']) : null;
242-
$data['variables'] = is_array($data['variables']) ?
243-
$data['variables'] : null;
244-
} else {
245-
return [];
250+
try {
251+
/** @var Http $request */
252+
if ($request->isPost()) {
253+
$data = $request->getContent() ? $this->jsonSerializer->unserialize($request->getContent()) : [];
254+
} elseif ($request->isGet()) {
255+
$data = $request->getParams();
256+
$data['variables'] = isset($data['variables']) ?
257+
$this->jsonSerializer->unserialize($data['variables']) : null;
258+
$data['variables'] = is_array($data['variables']) ?
259+
$data['variables'] : null;
260+
} else {
261+
$data = [];
262+
}
263+
} catch (\InvalidArgumentException $e) {
264+
throw new GraphQlInputException(__('Unable to parse the request.'), $e);
246265
}
247266

248267
return $data;

dev/tests/integration/testsuite/Magento/GraphQl/Controller/GraphQlControllerTest.php

Lines changed: 121 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?php
22
/**
3-
* Copyright © Magento, Inc. All rights reserved.
4-
* See COPYING.txt for license details.
3+
* Copyright 2018 Adobe
4+
* All Rights Reserved.
55
*/
66
declare(strict_types=1);
77

@@ -24,8 +24,6 @@
2424
*/
2525
class GraphQlControllerTest extends \Magento\TestFramework\Indexer\TestCase
2626
{
27-
const CONTENT_TYPE = 'application/json';
28-
2927
/** @var \Magento\Framework\ObjectManagerInterface */
3028
private $objectManager;
3129

@@ -175,7 +173,7 @@ public function testDispatchGetWithParameterizedVariables() : void
175173
id
176174
name
177175
sku
178-
}
176+
}
179177
}
180178
}
181179
QUERY;
@@ -223,12 +221,12 @@ public function testError() : void
223221
}
224222
])
225223
{
226-
items{
224+
items{
227225
attribute_code
228226
attribute_type
229227
entity_type
230-
}
231-
}
228+
}
229+
}
232230
}
233231
QUERY;
234232

@@ -265,4 +263,119 @@ public function testError() : void
265263
}
266264
}
267265
}
266+
267+
public function testDispatchOptions(): void
268+
{
269+
$this->request->setPathInfo('/graphql');
270+
$this->request->setMethod('OPTIONS');
271+
$response = $this->graphql->dispatch($this->request);
272+
self::assertEquals(200, $response->getStatusCode());
273+
self::assertEmpty($response->getContent());
274+
}
275+
276+
public function testDispatchGetWithoutQuery(): void
277+
{
278+
$this->request->setPathInfo('/graphql');
279+
$this->request->setMethod('GET');
280+
$response = $this->graphql->dispatch($this->request);
281+
self::assertEquals(400, $response->getStatusCode());
282+
$output = $this->jsonSerializer->unserialize($response->getContent());
283+
self::assertArrayHasKey('errors', $output);
284+
self::assertNotEmpty($output['errors']);
285+
self::assertArrayHasKey('message', $output['errors'][0]);
286+
self::assertStringStartsWith('Syntax Error:', $output['errors'][0]['message']);
287+
}
288+
289+
public function testDispatchGetWithInvalidQuery(): void
290+
{
291+
$query = <<<QUERY
292+
{
293+
products(filter: {sku: {eq: "simple1"}
294+
}
295+
QUERY;
296+
297+
$this->request->setPathInfo('/graphql');
298+
$this->request->setMethod('GET');
299+
$this->request->setQueryValue('query', $query);
300+
$response = $this->graphql->dispatch($this->request);
301+
self::assertEquals(400, $response->getStatusCode());
302+
$output = $this->jsonSerializer->unserialize($response->getContent());
303+
self::assertArrayHasKey('errors', $output);
304+
self::assertNotEmpty($output['errors']);
305+
self::assertArrayHasKey('message', $output['errors'][0]);
306+
self::assertStringStartsWith('Syntax Error:', $output['errors'][0]['message']);
307+
}
308+
309+
public function testDispatchPostWithoutQuery(): void
310+
{
311+
$this->request->setPathInfo('/graphql');
312+
$this->request->setMethod('POST');
313+
$headers = $this->objectManager->create(\Laminas\Http\Headers::class)
314+
->addHeaders(['Content-Type' => 'application/json']);
315+
$this->request->setHeaders($headers);
316+
$response = $this->graphql->dispatch($this->request);
317+
self::assertEquals(400, $response->getStatusCode());
318+
$output = $this->jsonSerializer->unserialize($response->getContent());
319+
self::assertArrayHasKey('errors', $output);
320+
self::assertNotEmpty($output['errors']);
321+
self::assertArrayHasKey('message', $output['errors'][0]);
322+
self::assertStringStartsWith('Syntax Error:', $output['errors'][0]['message']);
323+
}
324+
325+
public function testDispatchPostWithInvalidJson(): void
326+
{
327+
$query = <<<QUERY
328+
{
329+
products(filter: {sku: {eq: "simple1"}}) {
330+
items {
331+
id
332+
name
333+
sku
334+
}
335+
}
336+
}
337+
QUERY;
338+
$postData = ['query' => $query];
339+
340+
$this->request->setPathInfo('/graphql');
341+
$this->request->setMethod('POST');
342+
$this->request->setContent(http_build_query($postData));
343+
$headers = $this->objectManager->create(\Laminas\Http\Headers::class)
344+
->addHeaders(['Content-Type' => 'application/json']);
345+
$this->request->setHeaders($headers);
346+
$response = $this->graphql->dispatch($this->request);
347+
self::assertEquals(400, $response->getStatusCode());
348+
$output = $this->jsonSerializer->unserialize($response->getContent());
349+
self::assertArrayHasKey('errors', $output);
350+
self::assertNotEmpty($output['errors']);
351+
self::assertArrayHasKey('message', $output['errors'][0]);
352+
self::assertEquals('Unable to parse the request.', $output['errors'][0]['message']);
353+
}
354+
355+
public function testDispatchPostWithWrongContentType(): void
356+
{
357+
$query = <<<QUERY
358+
{
359+
products(filter: {sku: {eq: "simple1"}}) {
360+
items {
361+
id
362+
name
363+
sku
364+
}
365+
}
366+
}
367+
QUERY;
368+
$postData = ['query' => $query];
369+
370+
$this->request->setPathInfo('/graphql');
371+
$this->request->setMethod('POST');
372+
$this->request->setContent(json_encode($postData));
373+
$response = $this->graphql->dispatch($this->request);
374+
self::assertEquals(400, $response->getStatusCode());
375+
$output = $this->jsonSerializer->unserialize($response->getContent());
376+
self::assertArrayHasKey('errors', $output);
377+
self::assertNotEmpty($output['errors']);
378+
self::assertArrayHasKey('message', $output['errors'][0]);
379+
self::assertEquals('Request content type must be application/json', $output['errors'][0]['message']);
380+
}
268381
}

0 commit comments

Comments
 (0)