Skip to content

Commit 60bcc31

Browse files
authored
LYNX-533: cart.itemsV2.items.product.custom_attributesV2 returns a server error
1 parent d190dff commit 60bcc31

File tree

2 files changed

+385
-1
lines changed

2 files changed

+385
-1
lines changed

app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductCustomAttributes.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ function (AttributeInterface $customAttribute) {
106106
if (!array_key_exists($attributeCode, $productData)) {
107107
continue;
108108
}
109-
$attributeValue = $productData[$attributeCode];
109+
$attributeValue = $productData[$attributeCode] ?? "";
110110
if (is_array($attributeValue)) {
111111
$attributeValue = implode(',', $attributeValue);
112112
}
Lines changed: 384 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,384 @@
1+
<?php
2+
/**
3+
* ADOBE CONFIDENTIAL
4+
*
5+
* Copyright 2024 Adobe
6+
* All Rights Reserved.
7+
*
8+
* NOTICE: All information contained herein is, and remains
9+
* the property of Adobe and its suppliers, if any. The intellectual
10+
* and technical concepts contained herein are proprietary to Adobe
11+
* and its suppliers and are protected by all applicable intellectual
12+
* property laws, including trade secret and copyright laws.
13+
* Dissemination of this information or reproduction of this material
14+
* is strictly forbidden unless prior written permission is obtained
15+
* from Adobe.
16+
*/
17+
declare(strict_types=1);
18+
19+
namespace Magento\GraphQl\Quote;
20+
21+
use Magento\Catalog\Setup\CategorySetup;
22+
use Magento\Catalog\Test\Fixture\Attribute;
23+
use Magento\Catalog\Test\Fixture\MultiselectAttribute;
24+
use Magento\Catalog\Test\Fixture\SelectAttribute;
25+
use Magento\Catalog\Test\Fixture\Product as ProductFixture;
26+
use Magento\Eav\Model\Entity\Attribute\Backend\ArrayBackend;
27+
use Magento\Eav\Model\Entity\Attribute\Source\Table;
28+
use Magento\Eav\Test\Fixture\AttributeOption as AttributeOptionFixture;
29+
use Magento\Quote\Test\Fixture\AddProductToCart as AddProductToCartFixture;
30+
use Magento\Quote\Test\Fixture\GuestCart as GuestCartFixture;
31+
use Magento\Quote\Test\Fixture\QuoteIdMask as QuoteMaskFixture;
32+
use Magento\TestFramework\Fixture\DataFixture;
33+
use Magento\TestFramework\Fixture\DataFixtureStorage;
34+
use Magento\TestFramework\Fixture\DataFixtureStorageManager;
35+
use Magento\TestFramework\TestCase\GraphQlAbstract;
36+
37+
/**
38+
* Test for custom_attributesV2 (items & error) on cart.itemsV2.items.product
39+
*/
40+
class CartItemCustomAttributeTest extends GraphQlAbstract
41+
{
42+
/**
43+
* @var DataFixtureStorage
44+
*/
45+
private $fixtures;
46+
47+
/**
48+
* @inheritdoc
49+
*/
50+
protected function setUp(): void
51+
{
52+
parent::setUp();
53+
$this->fixtures = DataFixtureStorageManager::getStorage();
54+
}
55+
56+
#[
57+
DataFixture(
58+
Attribute::class,
59+
[
60+
'entity_type_id' => CategorySetup::CATALOG_PRODUCT_ENTITY_TYPE_ID,
61+
'attribute_code' => 'product_custom_attribute',
62+
'is_visible_on_front' => true
63+
],
64+
'varchar_custom_attribute'
65+
),
66+
DataFixture(
67+
MultiselectAttribute::class,
68+
[
69+
'entity_type_id' => CategorySetup::CATALOG_PRODUCT_ENTITY_TYPE_ID,
70+
'source_model' => Table::class,
71+
'backend_model' => ArrayBackend::class,
72+
'attribute_code' => 'product_custom_attribute_multiselect',
73+
'is_visible_on_front' => true
74+
],
75+
'multiselect_custom_attribute'
76+
),
77+
DataFixture(
78+
AttributeOptionFixture::class,
79+
[
80+
'entity_type' => CategorySetup::CATALOG_PRODUCT_ENTITY_TYPE_ID,
81+
'attribute_code' => '$multiselect_custom_attribute.attribute_code$',
82+
'label' => 'red',
83+
'sort_order' => 20
84+
],
85+
'multiselect_custom_attribute_option_1'
86+
),
87+
DataFixture(
88+
AttributeOptionFixture::class,
89+
[
90+
'entity_type' => CategorySetup::CATALOG_PRODUCT_ENTITY_TYPE_ID,
91+
'attribute_code' => '$multiselect_custom_attribute.attribute_code$',
92+
'sort_order' => 10,
93+
'label' => 'white',
94+
'is_default' => true
95+
],
96+
'multiselect_custom_attribute_option_2'
97+
),
98+
DataFixture(
99+
ProductFixture::class,
100+
[
101+
'custom_attributes' => [
102+
[
103+
'attribute_code' => '$varchar_custom_attribute.attribute_code$',
104+
'value' => ''
105+
],
106+
[
107+
'attribute_code' => '$multiselect_custom_attribute.attribute_code$',
108+
'selected_options' => [],
109+
],
110+
],
111+
],
112+
'product'
113+
),
114+
DataFixture(GuestCartFixture::class, ['reserved_order_id' => 'test_quote'], 'cart'),
115+
DataFixture(AddProductToCartFixture::class, ['cart_id' => '$cart.id$', 'product_id' => '$product.id$']),
116+
DataFixture(QuoteMaskFixture::class, ['cart_id' => '$cart.id$'], 'quoteIdMask'),
117+
]
118+
public function testEmptyErrorsOnCartItemCustomAttributeWithEmptyValue(): void
119+
{
120+
$maskedQuoteId = $this->fixtures->get('quoteIdMask')->getMaskedId();
121+
$productName = $this->fixtures->get('product')->getName();
122+
$cartQuery = $this->getCartQuery($maskedQuoteId);
123+
$response = $this->graphQlQuery($cartQuery);
124+
self::assertEquals(
125+
[
126+
'cart' => [
127+
'itemsV2' => [
128+
'items' => [
129+
0 => [
130+
'product' => [
131+
'name' => $productName,
132+
'custom_attributesV2' => [
133+
'items' => [],
134+
'errors' => []
135+
]
136+
]
137+
]
138+
]
139+
]
140+
]
141+
],
142+
$response
143+
);
144+
}
145+
146+
#[
147+
DataFixture(
148+
Attribute::class,
149+
[
150+
'entity_type_id' => CategorySetup::CATALOG_PRODUCT_ENTITY_TYPE_ID,
151+
'attribute_code' => 'product_custom_attribute',
152+
'is_comparable' => 1,
153+
'is_visible_on_front' => 1
154+
],
155+
'varchar_custom_attribute'
156+
),
157+
DataFixture(
158+
MultiselectAttribute::class,
159+
[
160+
'entity_type_id' => CategorySetup::CATALOG_PRODUCT_ENTITY_TYPE_ID,
161+
'source_model' => Table::class,
162+
'backend_model' => ArrayBackend::class,
163+
'attribute_code' => 'product_custom_attribute_multiselect',
164+
'is_visible_on_front' => 1
165+
],
166+
'multiselect_custom_attribute'
167+
),
168+
DataFixture(
169+
AttributeOptionFixture::class,
170+
[
171+
'entity_type' => CategorySetup::CATALOG_PRODUCT_ENTITY_TYPE_ID,
172+
'attribute_code' => '$multiselect_custom_attribute.attribute_code$',
173+
'label' => 'red',
174+
'sort_order' => 20
175+
],
176+
'multiselect_custom_attribute_option_1'
177+
),
178+
DataFixture(
179+
AttributeOptionFixture::class,
180+
[
181+
'entity_type' => CategorySetup::CATALOG_PRODUCT_ENTITY_TYPE_ID,
182+
'attribute_code' => '$multiselect_custom_attribute.attribute_code$',
183+
'sort_order' => 10,
184+
'label' => 'white',
185+
'is_default' => true
186+
],
187+
'multiselect_custom_attribute_option_2'
188+
),
189+
DataFixture(
190+
ProductFixture::class,
191+
[
192+
'custom_attributes' => [
193+
[
194+
'attribute_code' => '$varchar_custom_attribute.attribute_code$',
195+
'value' => 'test value'
196+
],
197+
[
198+
'attribute_code' => '$multiselect_custom_attribute.attribute_code$',
199+
'selected_options' => [
200+
['value' => '$multiselect_custom_attribute_option_1.value$'],
201+
['value' => '$multiselect_custom_attribute_option_2.value$']
202+
],
203+
],
204+
],
205+
],
206+
'product'
207+
),
208+
DataFixture(GuestCartFixture::class, ['reserved_order_id' => 'test_quote'], 'cart'),
209+
DataFixture(AddProductToCartFixture::class, ['cart_id' => '$cart.id$', 'product_id' => '$product.id$']),
210+
DataFixture(QuoteMaskFixture::class, ['cart_id' => '$cart.id$'], 'quoteIdMask'),
211+
]
212+
public function testEmptyErrorsOnCartItemCustomAttributeWithNonEmptyValue(): void
213+
{
214+
$maskedQuoteId = $this->fixtures->get('quoteIdMask')->getMaskedId();
215+
$productName = $this->fixtures->get('product')->getName();
216+
$multiselectCustomAttrOption1 = $this->fixtures->get('multiselect_custom_attribute_option_1')->getValue();
217+
$multiselectCustomAttrOption2 = $this->fixtures->get('multiselect_custom_attribute_option_2')->getValue();
218+
$cartQuery = $this->getCartQuery($maskedQuoteId);
219+
$response = $this->graphQlQuery($cartQuery);
220+
self::assertEquals(
221+
[
222+
'cart' => [
223+
'itemsV2' => [
224+
'items' => [
225+
0 => [
226+
'product' => [
227+
'name' => $productName,
228+
'custom_attributesV2' => [
229+
'items' => [
230+
0 => [
231+
'code' => 'product_custom_attribute',
232+
'value' => 'test value'
233+
],
234+
1 => [
235+
'code' => 'product_custom_attribute_multiselect',
236+
'selected_options' => [
237+
0 => [
238+
'value' => $multiselectCustomAttrOption2,
239+
'label' => 'white'
240+
],
241+
1 => [
242+
'value' => $multiselectCustomAttrOption1,
243+
'label' => 'red'
244+
]
245+
]
246+
]
247+
],
248+
'errors' => []
249+
]
250+
]
251+
]
252+
]
253+
]
254+
]
255+
],
256+
$response
257+
);
258+
}
259+
260+
#[
261+
DataFixture(
262+
SelectAttribute::class,
263+
[
264+
'entity_type_id' => CategorySetup::CATALOG_PRODUCT_ENTITY_TYPE_ID,
265+
'source_model' => Table::class,
266+
'backend_model' => ArrayBackend::class,
267+
'attribute_code' => 'product_custom_attribute_select',
268+
'is_visible_on_front' => true
269+
],
270+
'select_custom_attribute'
271+
),
272+
DataFixture(
273+
AttributeOptionFixture::class,
274+
[
275+
'entity_type' => CategorySetup::CATALOG_PRODUCT_ENTITY_TYPE_ID,
276+
'attribute_code' => '$select_custom_attribute.attribute_code$',
277+
'label' => 'red',
278+
'sort_order' => 20
279+
],
280+
'select_custom_attribute_option_1'
281+
),
282+
DataFixture(
283+
AttributeOptionFixture::class,
284+
[
285+
'entity_type' => CategorySetup::CATALOG_PRODUCT_ENTITY_TYPE_ID,
286+
'attribute_code' => '$select_custom_attribute.attribute_code$',
287+
'sort_order' => 10,
288+
'label' => 'white',
289+
'is_default' => true
290+
],
291+
'select_custom_attribute_option_2'
292+
),
293+
DataFixture(
294+
ProductFixture::class,
295+
[
296+
'custom_attributes' => [
297+
[
298+
'attribute_code' => '$select_custom_attribute.attribute_code$',
299+
'selected_options' => [
300+
['value' => '0']
301+
],
302+
],
303+
],
304+
],
305+
'product'
306+
),
307+
DataFixture(GuestCartFixture::class, ['reserved_order_id' => 'test_quote'], 'cart'),
308+
DataFixture(AddProductToCartFixture::class, ['cart_id' => '$cart.id$', 'product_id' => '$product.id$']),
309+
DataFixture(QuoteMaskFixture::class, ['cart_id' => '$cart.id$'], 'quoteIdMask'),
310+
]
311+
public function testEmptyErrorsOnCartItemCustomAttributeWithNoOptionSelected(): void
312+
{
313+
$maskedQuoteId = $this->fixtures->get('quoteIdMask')->getMaskedId();
314+
$productName = $this->fixtures->get('product')->getName();
315+
$cartQuery = $this->getCartQuery($maskedQuoteId);
316+
$response = $this->graphQlQuery($cartQuery);
317+
self::assertEquals(
318+
[
319+
'cart' => [
320+
'itemsV2' => [
321+
'items' => [
322+
0 => [
323+
'product' => [
324+
'name' => $productName,
325+
'custom_attributesV2' => [
326+
'items' => [
327+
0 => [
328+
'code' => 'product_custom_attribute_select',
329+
'selected_options' => []
330+
]
331+
],
332+
'errors' => []
333+
]
334+
]
335+
]
336+
]
337+
]
338+
]
339+
],
340+
$response
341+
);
342+
}
343+
344+
/**
345+
* Returns cart query with product - custom_attributesV2
346+
*
347+
* @param string $maskedQuoteId
348+
* @return string
349+
*/
350+
private function getCartQuery(string $maskedQuoteId): string
351+
{
352+
return <<<QUERY
353+
query {
354+
cart(cart_id: "{$maskedQuoteId}") {
355+
itemsV2 {
356+
items {
357+
product {
358+
name
359+
custom_attributesV2(filters: {is_visible_on_front: true}) {
360+
items {
361+
code
362+
...on AttributeValue {
363+
value
364+
}
365+
...on AttributeSelectedOptions {
366+
selected_options {
367+
value
368+
label
369+
}
370+
}
371+
}
372+
errors {
373+
message
374+
type
375+
}
376+
}
377+
}
378+
}
379+
}
380+
}
381+
}
382+
QUERY;
383+
}
384+
}

0 commit comments

Comments
 (0)