Skip to content

Commit 1422e63

Browse files
committed
ACP2E-2098: addressed hard depency, added integration test
1 parent dadbe14 commit 1422e63

File tree

2 files changed

+207
-18
lines changed

2 files changed

+207
-18
lines changed

app/code/Magento/QuoteGraphQl/Plugin/ProductAttributesExtender.php

Lines changed: 64 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,26 @@
88
namespace Magento\QuoteGraphQl\Plugin;
99

1010
use Magento\Catalog\Model\ResourceModel\Product\Attribute\CollectionFactory as AttributeCollectionFactory;
11-
use Magento\Eav\Model\Validator\Attribute\Code;
12-
use Magento\Framework\App\ObjectManager;
1311
use Magento\Framework\GraphQl\Query\Fields;
12+
use Magento\Framework\Validator\StringLength;
1413
use Magento\Framework\Validator\ValidateException;
14+
use Magento\Framework\Validator\ValidatorChain;
1515
use Magento\Quote\Model\Quote\Config as QuoteConfig;
1616

1717
/**
1818
* Class for extending product attributes for quote.
1919
*/
2020
class ProductAttributesExtender
2121
{
22+
/**
23+
* Validation pattern for attribute code
24+
*/
25+
private const VALIDATION_RULE_PATTERN = '/^[a-zA-Z]+[a-zA-Z0-9_]*$/u';
26+
27+
private const ATTRIBUTE_CODE_MAX_LENGTH = 60;
28+
29+
private const ATTRIBUTE_CODE_MIN_LENGTH = 1;
30+
2231
/**
2332
* @var Fields
2433
*/
@@ -30,27 +39,29 @@ class ProductAttributesExtender
3039
private $attributeCollectionFactory;
3140

3241
/**
33-
* @var Code
42+
* @var string
43+
*/
44+
private $fieldsHash = '';
45+
46+
/**
47+
* @var array
3448
*/
35-
private Code $attributeCodeValidator;
49+
private $attributes;
3650

3751
/**
3852
* @param Fields $fields
3953
* @param AttributeCollectionFactory $attributeCollectionFactory
40-
* @param Code|null $attributeCodeValidator
4154
*/
4255
public function __construct(
4356
Fields $fields,
44-
AttributeCollectionFactory $attributeCollectionFactory,
45-
Code $attributeCodeValidator = null
57+
AttributeCollectionFactory $attributeCollectionFactory
4658
) {
4759
$this->fields = $fields;
4860
$this->attributeCollectionFactory = $attributeCollectionFactory;
49-
$this->attributeCodeValidator = $attributeCodeValidator ?? ObjectManager::getInstance()->get(Code::class);
5061
}
5162

5263
/**
53-
* Get only attribute code that pass validation
64+
* Get only attribute codes that pass validation
5465
*
5566
* @return array
5667
*/
@@ -60,31 +71,66 @@ private function getValidatedAttributeCodes(): array
6071
}
6172

6273
/**
63-
* @param string|int $code
74+
* Validate attribute code
75+
*
76+
* @param string|int $attributeCode
6477
* @return bool
6578
* @throws ValidateException
6679
*/
67-
private function validateAttributeCode(string|int $code)
80+
private function validateAttributeCode(string|int $attributeCode): bool
6881
{
69-
return $this->attributeCodeValidator->isValid((string)$code);
82+
$attributeCode = trim((string)$attributeCode);
83+
if (strlen($attributeCode) > 0
84+
&& !preg_match(self::VALIDATION_RULE_PATTERN, $attributeCode)
85+
) {
86+
return false;
87+
}
88+
89+
$minLength = self::ATTRIBUTE_CODE_MIN_LENGTH;
90+
$maxLength = self::ATTRIBUTE_CODE_MAX_LENGTH;
91+
$isAllowedLength = ValidatorChain::is(
92+
$attributeCode,
93+
StringLength::class,
94+
['min' => $minLength, 'max' => $maxLength]
95+
);
96+
if (!$isAllowedLength) {
97+
return false;
98+
}
99+
100+
return true;
70101
}
71102

72103
/**
73-
* Add requested product attributes.
104+
* Get attributes collection based on validated codes
74105
*
75-
* @param QuoteConfig $subject
76-
* @param array $result
77106
* @return array
78-
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
79107
*/
80-
public function afterGetProductAttributes(QuoteConfig $subject, array $result): array
108+
private function getAttributeCollection()
81109
{
82110
$attributeCollection = $this->attributeCollectionFactory->create()
83111
->removeAllFieldsFromSelect()
84112
->addFieldToSelect('attribute_code')
85113
->setCodeFilter($this->getValidatedAttributeCodes())
86114
->load();
87-
$attributes = $attributeCollection->getColumnValues('attribute_code');
115+
return $attributeCollection->getColumnValues('attribute_code');
116+
}
117+
118+
/**
119+
* Add requested product attributes.
120+
*
121+
* @param QuoteConfig $subject
122+
* @param array $result
123+
* @return array
124+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
125+
*/
126+
public function afterGetProductAttributes(QuoteConfig $subject, array $result): array
127+
{
128+
$hash = hash('sha256', json_encode($this->fields->getFieldsUsedInQuery()));
129+
if (!$this->fieldsHash || $this->fieldsHash !== $hash) {
130+
$this->fieldsHash = hash('sha256', json_encode($this->fields->getFieldsUsedInQuery()));
131+
$this->attributes = $this->getAttributeCollection();
132+
}
133+
$attributes = $this->attributes;
88134

89135
return array_unique(array_merge($result, $attributes));
90136
}
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
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\Quote;
9+
10+
use Magento\Catalog\Api\ProductRepositoryInterface;
11+
use Magento\Catalog\Model\Product;
12+
use Magento\Catalog\Test\Fixture\Attribute as AttributeFixture;
13+
use Magento\Catalog\Test\Fixture\Product as ProductFixture;
14+
use Magento\Framework\Exception\NoSuchEntityException;
15+
use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface;
16+
use Magento\Quote\Test\Fixture\GuestCart as GuestCartFixture;
17+
use Magento\TestFramework\Fixture\DataFixture;
18+
use Magento\TestFramework\Fixture\DataFixtureStorage;
19+
use Magento\TestFramework\Fixture\DataFixtureStorageManager;
20+
use Magento\TestFramework\Helper\Bootstrap;
21+
use Magento\TestFramework\TestCase\GraphQlAbstract;
22+
23+
/**
24+
* Get add to cart through GraphQl query and variables
25+
*/
26+
class AddProductsToCartWithVariablesTest extends GraphQlAbstract
27+
{
28+
/**
29+
* @var ProductRepositoryInterface
30+
*/
31+
private $productRepository;
32+
33+
/**
34+
* @var DataFixtureStorage
35+
*/
36+
private $fixtures;
37+
38+
/**
39+
* @var QuoteIdToMaskedQuoteIdInterface
40+
*/
41+
private $quoteIdToMaskedQuoteIdInterface;
42+
43+
/**
44+
* @inheritdoc
45+
*/
46+
protected function setUp(): void
47+
{
48+
$objectManager = Bootstrap::getObjectManager();
49+
50+
$this->productRepository = $objectManager->get(ProductRepositoryInterface::class);
51+
$this->quoteIdToMaskedQuoteIdInterface = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class);
52+
$this->fixtures = $objectManager->get(DataFixtureStorageManager::class)->getStorage();
53+
}
54+
55+
/**
56+
* @throws NoSuchEntityException
57+
* @throws \Exception
58+
*/
59+
#[
60+
DataFixture(AttributeFixture::class, ['attribute_code' => 'prod_attr'], as: 'attr'),
61+
DataFixture(ProductFixture::class, ['attribute_set_id' => 13], as: 'product'),
62+
DataFixture(GuestCartFixture::class, as: 'cart'),
63+
]
64+
public function testAddProductsToEmptyCartWithVariables(): void
65+
{
66+
/** @var Product $product */
67+
$product = $this->fixtures->get('product');
68+
$product->setData('prod_attr', 'default_value');
69+
$this->productRepository->save($product);
70+
$cart = $this->fixtures->get('cart');
71+
$maskedQuoteId = $this->quoteIdToMaskedQuoteIdInterface->execute((int) $cart->getId());
72+
$query = $this->getAddToCartMutation();
73+
$variables = $this->getAddToCartVariables($maskedQuoteId, 1, $product->getSku());
74+
$response = $this->graphQlMutation($query, $variables);
75+
$result = $response['addProductsToCart'];
76+
self::assertEmpty($result['user_errors']);
77+
self::assertCount(1, $result['cart']['items']);
78+
79+
$cartItem = $result['cart']['items'][0];
80+
self::assertEquals($product->getSku(), $cartItem['product']['sku']);
81+
self::assertEquals(1, $cartItem['quantity']);
82+
self::assertEquals($product->getFinalPrice(), $cartItem['prices']['price']['value']);
83+
}
84+
85+
/**
86+
* Returns GraphQl mutation for adding item to cart
87+
*
88+
* @return string
89+
*/
90+
private function getAddToCartMutation(): string
91+
{
92+
return <<<MUTATION
93+
mutation (\$cartId: String!, \$products: [CartItemInput!]!) {
94+
addProductsToCart(cartId: \$cartId, cartItems: \$products) {
95+
cart {
96+
id
97+
items {
98+
uid
99+
quantity
100+
product {
101+
sku
102+
name
103+
thumbnail {
104+
url
105+
__typename
106+
}
107+
__typename
108+
}
109+
prices {
110+
price {
111+
value
112+
currency
113+
}
114+
}
115+
}
116+
}
117+
user_errors {
118+
code
119+
message
120+
}
121+
}
122+
}
123+
MUTATION;
124+
}
125+
126+
private function getAddToCartVariables(
127+
string $maskedQuoteId,
128+
int $qty,
129+
string $sku
130+
): array {
131+
return
132+
[
133+
'cartId' => $maskedQuoteId,
134+
'products' => [
135+
[
136+
'sku' => $sku,
137+
'parent_sku' => $sku,
138+
'quantity' => $qty
139+
]
140+
]
141+
];
142+
}
143+
}

0 commit comments

Comments
 (0)