Skip to content

Commit 9ff22de

Browse files
Merge pull request #7130 from magento-honey-badgers/PWA-2137-graphql-scalar-typecast
[honey] PWA-2137: [GraphQL] Need to bypass webonyx type validation
2 parents 207c959 + cd64011 commit 9ff22de

File tree

8 files changed

+573
-20
lines changed

8 files changed

+573
-20
lines changed
Lines changed: 274 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,274 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\GraphQl;
9+
10+
use Magento\Catalog\Api\ProductRepositoryInterface;
11+
use Magento\GraphQl\Quote\GetMaskedQuoteIdByReservedOrderId;
12+
use Magento\TestFramework\Helper\Bootstrap;
13+
use Magento\TestFramework\TestCase\GraphQlAbstract;
14+
15+
class GraphQlTypeValidationTest extends GraphQlAbstract
16+
{
17+
/**
18+
* @var ProductRepositoryInterface
19+
*/
20+
private $productRepository;
21+
22+
/**
23+
* @var GetMaskedQuoteIdByReservedOrderId
24+
*/
25+
private $getMaskedQuoteIdByReservedOrderId;
26+
27+
protected function setUp(): void
28+
{
29+
$objectManager = Bootstrap::getObjectManager();
30+
$this->getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class);
31+
$this->productRepository = $objectManager->get(ProductRepositoryInterface::class);
32+
}
33+
34+
/**
35+
*
36+
* Tests that field expecting an Int type ; but Float is provided
37+
*
38+
* @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php
39+
*/
40+
public function testIntegerExpectedWhenFloatProvided()
41+
{
42+
$query
43+
= <<<'QUERY'
44+
query GetProductsQuery($pageSize: Int, $filterInput: ProductAttributeFilterInput, $currentPage:Int ) {
45+
products(
46+
filter: $filterInput
47+
pageSize: $pageSize
48+
currentPage: $currentPage
49+
50+
) {
51+
items {
52+
sku
53+
name
54+
id
55+
}
56+
}
57+
}
58+
QUERY;
59+
60+
$variables = [
61+
62+
'filterInput' => [
63+
'sku' => [
64+
'eq' => 'simple_product',
65+
],
66+
],
67+
'pageSize' => 1,
68+
'currentPage' => 1.1
69+
];
70+
$this->expectException(\Exception::class);
71+
$this->expectExceptionMessage('Variable "$currentPage" got invalid value 1.1; Expected type Int; ' .
72+
'Int cannot represent non-integer value: 1.1');
73+
$this->graphQlQuery($query, $variables);
74+
}
75+
76+
/**
77+
* Tests that field expects an Float type ; but String is provided
78+
*
79+
* @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php
80+
* @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php
81+
*/
82+
public function testFloatExpectedWhenStringProvided()
83+
{
84+
$sku = 'simple_product';
85+
/** @var \Magento\Catalog\Model\Product $product */
86+
$product = $this->productRepository->get($sku, false, null, true);
87+
$cartId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote');
88+
$query = $this->addProductsToCart();
89+
$variables = [
90+
'cartId' => $cartId,
91+
'sku' => $sku,
92+
'quantity' => '1.9'
93+
];
94+
$response = $this->graphQlMutation($query, $variables);
95+
$this->assertArrayNotHasKey('errors', $response);
96+
$this->assertArrayHasKey('addProductsToCart', $response);
97+
$this->assertCount(1, $response['addProductsToCart']['cart']['items']);
98+
$this->assertEquals($product->getSku(), $response['addProductsToCart']['cart']['items'][0]['product']['sku']);
99+
$this->assertEquals(1.9, $response['addProductsToCart']['cart']['items'][0]['quantity']);
100+
}
101+
102+
/**
103+
* Verify that query is resolved even when field expecting an Int is provided with String type data
104+
*
105+
* @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php
106+
* @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php
107+
* @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php
108+
*/
109+
public function testIntegerExpectedWhenStringProvided()
110+
{
111+
$cartId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote');
112+
$quantity = 5;
113+
$itemId = $this->getCartItemId($cartId);
114+
$query
115+
= <<<'MUTATION'
116+
mutation updateItemQuantity($cartId: String!, $itemId: Int!, $quantity: Float!)
117+
{
118+
updateCartItems(input:
119+
{cart_id: $cartId,
120+
cart_items:
121+
[
122+
{cart_item_id: $itemId,
123+
quantity: $quantity}]}
124+
)
125+
{
126+
cart
127+
{
128+
id
129+
items {id product {sku id } quantity}
130+
}
131+
}
132+
}
133+
MUTATION;
134+
135+
// $itemId expects an integer type, but a string value is provided
136+
$variables = [
137+
'cartId' => $cartId,
138+
'itemId'=> "{$itemId}",
139+
'quantity'=> $quantity
140+
];
141+
$response = $this->graphQlMutation($query, $variables);
142+
$this->assertArrayNotHasKey('errors', $response);
143+
$this->assertArrayHasKey('updateCartItems', $response);
144+
$this->assertCount(1, $response['updateCartItems']['cart']['items']);
145+
$this->assertEquals('simple_product', $response['updateCartItems']['cart']['items'][0]['product']['sku']);
146+
$this->assertEquals(5, $response['updateCartItems']['cart']['items'][0]['quantity']);
147+
$this->assertEquals($itemId, $response['updateCartItems']['cart']['items'][0]['id']);
148+
}
149+
150+
/**
151+
* Verify that query doesn't return error when an integer is passed for a field where string is expected
152+
*
153+
* @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product_with_numeric_sku.php
154+
* @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php
155+
*/
156+
public function testStringExpectedWhenFloatProvided()
157+
{
158+
$cartId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote');
159+
$query = $this->addProductsToCart();
160+
161+
// sku is expecting a string ; but an float s given.
162+
// And quantity is expecting a float, but value is passed as string
163+
$variables = [
164+
'cartId' => $cartId,
165+
'sku' => 123.78,
166+
'quantity' => '5.60'
167+
];
168+
$response = $this->graphQlMutation($query, $variables);
169+
$this->assertArrayNotHasKey('errors', $response);
170+
$this->assertArrayHasKey('addProductsToCart', $response);
171+
$this->assertCount(1, $response['addProductsToCart']['cart']['items']);
172+
$this->assertEquals('123.78', $response['addProductsToCart']['cart']['items'][0]['product']['sku']);
173+
$this->assertEquals(5.60, $response['addProductsToCart']['cart']['items'][0]['quantity']);
174+
}
175+
176+
/**
177+
* Verify that query doesn't return error when an integer is passed for a field where string is expected
178+
*
179+
* @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product_with_numeric_sku.php
180+
* @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php
181+
*/
182+
public function testStringExpectedWhenArrayProvided()
183+
{
184+
$cartId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote');
185+
$query = $this->addProductsToCart();
186+
187+
// sku is expecting a string ; but an array is passed.
188+
// And quantity is expecting a float, but value is passed as string
189+
$variables = [
190+
'cartId' => $cartId,
191+
'sku' => ['123.78'],
192+
'quantity' => '5.60'
193+
];
194+
$this->expectException(\Exception::class);
195+
$this->expectExceptionMessage('Variable "$sku" got invalid value ["123.78"]; Expected type String; ' .
196+
'String cannot represent a non string value: ["123.78"]');
197+
$this->graphQlMutation($query, $variables);
198+
}
199+
200+
/**
201+
* Verify that query doesn't return error when an integer is passed for a field where string is expected
202+
*
203+
* @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product_with_numeric_sku.php
204+
* @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php
205+
*/
206+
public function testFloatExpectedWhenNonNumericStringProvided()
207+
{
208+
$cartId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote');
209+
$query = $this->addProductsToCart();
210+
211+
// quantity is expecting a float, but a non-numeric string is passed
212+
$variables = [
213+
'cartId' => $cartId,
214+
'sku' => '123.78',
215+
'quantity' => 'ten'
216+
];
217+
$this->expectException(\Exception::class);
218+
$this->expectExceptionMessage('Variable "$quantity" got invalid value "ten"; Expected type Float; ' .
219+
'Float cannot represent non numeric value: ten');
220+
$this->graphQlMutation($query, $variables);
221+
}
222+
223+
/**
224+
* Query the cart to get the cart item id
225+
*
226+
* @param string $cartId
227+
* @return string
228+
* @throws \Exception
229+
*/
230+
private function getCartItemId(string $cartId): string
231+
{
232+
$cartQuery = <<<QUERY
233+
{
234+
cart(cart_id: "$cartId") {
235+
id
236+
items {
237+
id
238+
}
239+
}
240+
}
241+
QUERY;
242+
$result = $this->graphQlQuery($cartQuery);
243+
$this->assertArrayNotHasKey('errors', $result);
244+
$this->assertCount(1, $result['cart']['items']);
245+
return $result['cart']['items'][0]['id'];
246+
}
247+
248+
/**
249+
* @return string
250+
*/
251+
private function addProductsToCart():string
252+
{
253+
return <<<'MUTATION'
254+
mutation AddItemsToCart($cartId: String!, $sku: String!, $quantity: Float!)
255+
{
256+
addProductsToCart (
257+
cartId:$cartId
258+
cartItems:[
259+
{
260+
sku:$sku
261+
quantity:$quantity
262+
}]
263+
)
264+
{
265+
cart {
266+
id
267+
total_quantity
268+
items { product {sku id } quantity}
269+
}
270+
}
271+
}
272+
MUTATION;
273+
}
274+
}

dev/tests/integration/testsuite/Magento/Framework/GraphQl/GraphQlConfigTest.php

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,14 @@
2323
*/
2424
class GraphQlConfigTest extends \PHPUnit\Framework\TestCase
2525
{
26-
/** @var \Magento\Framework\GraphQl\Config */
26+
/** @var \Magento\Framework\GraphQl\Config */
2727
private $model;
2828

2929
protected function setUp(): void
3030
{
3131
/** @var ObjectManagerInterface $objectManager */
3232
$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
33-
/** @var Cache $cache */
33+
/** @var Cache $cache */
3434
$cache = $objectManager->get(Cache::class);
3535
$cache->clean();
3636
$fileResolverMock = $this->getMockBuilder(
@@ -48,16 +48,16 @@ protected function setUp(): void
4848
['fileResolver' => $fileResolverMock]
4949
);
5050
$reader = $objectManager->create(
51-
// phpstan:ignore
51+
// phpstan:ignore
5252
\Magento\Framework\GraphQlSchemaStitching\Reader::class,
5353
['readers' => ['graphql_reader' => $graphQlReader]]
5454
);
5555
$data = $objectManager->create(
56-
// phpstan:ignore
57-
\Magento\Framework\GraphQl\Config\Data ::class,
56+
// phpstan:ignore
57+
\Magento\Framework\GraphQl\Config\Data::class,
5858
['reader' => $reader]
5959
);
60-
$this->model = $objectManager->create(\Magento\Framework\GraphQl\Config::class, ['data' =>$data]);
60+
$this->model = $objectManager->create(\Magento\Framework\GraphQl\Config::class, ['data' =>$data]);
6161
}
6262

6363
/**
@@ -87,19 +87,19 @@ public function testGraphQlTypeAndFieldConfigStructure()
8787
]
8888
];
8989
$this->assertResponseFields($expectedOutputArray['Query']['fields'][$fieldKey], $fieldAssertionMap);
90-
/** @var \Magento\Framework\GraphQl\Config\Element\Argument $queryFieldArguments */
90+
/** @var \Magento\Framework\GraphQl\Config\Element\Argument[] $queryFieldArguments */
9191
$queryFieldArguments = $queryFields[$fieldKey]->getArguments();
9292
foreach (array_keys($queryFieldArguments) as $argumentKey) {
9393
$argumentAssertionMap = [
94-
['response_field' => 'name', 'expected_value' => $queryFieldArguments[$argumentKey]->getName()],
95-
['response_field' => 'type', 'expected_value' => $queryFieldArguments[$argumentKey]->getTypeName()],
96-
['response_field' => 'description', 'expected_value' => $queryFieldArguments[$argumentKey]
97-
->getDescription()],
98-
['response_field' => 'required', 'expected_value' => $queryFieldArguments[$argumentKey]
99-
->isRequired()],
100-
['response_field' => 'isList', 'expected_value' => $queryFieldArguments[$argumentKey]->isList()],
101-
['response_field' => 'itemsRequired', 'expected_value' => $queryFieldArguments[$argumentKey]
102-
->areItemsRequired()]
94+
['response_field' => 'name', 'expected_value' => $queryFieldArguments[$argumentKey]->getName()],
95+
['response_field' => 'type', 'expected_value' => $queryFieldArguments[$argumentKey]->getTypeName()],
96+
['response_field' => 'description', 'expected_value' => $queryFieldArguments[$argumentKey]
97+
->getDescription()],
98+
['response_field' => 'required', 'expected_value' => $queryFieldArguments[$argumentKey]
99+
->isRequired()],
100+
['response_field' => 'isList', 'expected_value' => $queryFieldArguments[$argumentKey]->isList()],
101+
['response_field' => 'itemsRequired', 'expected_value' => $queryFieldArguments[$argumentKey]
102+
->areItemsRequired()]
103103
];
104104
$this->assertResponseFields(
105105
$expectedOutputArray['Query']['fields'][$fieldKey]['arguments'][$argumentKey],
@@ -121,7 +121,7 @@ public function testGraphQlEnumTypeConfigStructure()
121121
$queryEnum = 'PriceAdjustmentDescriptionEnum';
122122
/** @var \Magento\Framework\GraphQl\Config\Element\Enum $outputEnum */
123123
$outputEnum = $this->model->getConfigElement($queryEnum);
124-
/** @var EnumValue $outputEnumValues */
124+
/** @var EnumValue[] $outputEnumValues */
125125
$outputEnumValues = $outputEnum->getValues();
126126
$expectedOutputArray = require __DIR__ . '/_files/query_array_output.php';
127127
$this->assertEquals($outputEnum->getName(), $queryEnum);
@@ -154,7 +154,7 @@ public function testGraphQlTypeThatImplementsInterface()
154154
$expectedOutputArray = require __DIR__ . '/_files/query_array_output.php';
155155
$this->assertEquals($outputInterface->getName(), $typeThatImplements);
156156
$outputInterfaceValues = $outputInterface->getInterfaces();
157-
/** @var \Magento\Framework\GraphQl\Config\Element\Field $outputInterfaceFields */
157+
/** @var \Magento\Framework\GraphQl\Config\Element\Field[] $outputInterfaceFields */
158158
$outputInterfaceFields =$outputInterface->getFields();
159159
foreach (array_keys($outputInterfaceValues) as $outputInterfaceValue) {
160160
$this->assertEquals(

0 commit comments

Comments
 (0)