Skip to content

Commit ee2ffab

Browse files
wip44850svera
andauthored
LYNX-405-1:GraphQL Error: Unsupported 'file' type in customizable opt… (#241)
* LYNX-405-1:GraphQL Error: Unsupported 'file' type in customizable options query * LYNX-405: Add test for cart query * LYNX-405:updated review comments * LYNX-405:update review comment --------- Co-authored-by: Sergio Vera <svera@adobe.com>
1 parent fcc6f0a commit ee2ffab

File tree

4 files changed

+332
-0
lines changed

4 files changed

+332
-0
lines changed
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
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+
18+
declare(strict_types=1);
19+
20+
namespace Magento\QuoteGraphQl\Model\CartItem\DataProvider\CustomizableOptionValue;
21+
22+
use Magento\Catalog\Model\Product\Option;
23+
use Magento\Catalog\Model\Product\Option\Type\Text as TextOptionType;
24+
use Magento\Quote\Model\Quote\Item as QuoteItem;
25+
use Magento\Quote\Model\Quote\Item\Option as SelectedOption;
26+
use Magento\QuoteGraphQl\Model\CartItem\DataProvider\CustomizableOptionValueInterface;
27+
28+
class File implements CustomizableOptionValueInterface
29+
{
30+
/**
31+
* @param PriceUnitLabel $priceUnitLabel
32+
*/
33+
public function __construct(
34+
private readonly PriceUnitLabel $priceUnitLabel
35+
) {
36+
}
37+
38+
/**
39+
* @inheritdoc
40+
*/
41+
public function getData(
42+
QuoteItem $cartItem,
43+
Option $option,
44+
SelectedOption $selectedOption
45+
): array {
46+
/** @var TextOptionType $optionTypeRenderer */
47+
$optionTypeRenderer = $option->groupFactory($option->getType());
48+
$optionTypeRenderer->setOption($option);
49+
$priceValueUnits = $this->priceUnitLabel->getData($option->getPriceType());
50+
$optionTypeRenderer->setData('configuration_item_option', $selectedOption);
51+
$value = $optionTypeRenderer->getFormattedOptionValue($selectedOption->getValue());
52+
$selectedOptionValueData = [
53+
'id' => $selectedOption->getId(),
54+
'label' => $option->getTitle(),
55+
'value' => $value,
56+
'price' => [
57+
'type' => strtoupper($option->getPriceType()),
58+
'units' => $priceValueUnits,
59+
'value' => $option->getPrice(),
60+
],
61+
];
62+
return [$selectedOptionValueData];
63+
}
64+
}

app/code/Magento/QuoteGraphQl/etc/di.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
<item name="radio" xsi:type="string">Magento\QuoteGraphQl\Model\CartItem\DataProvider\CustomizableOptionValue\Dropdown</item>
3232
<item name="checkbox" xsi:type="string">Magento\QuoteGraphQl\Model\CartItem\DataProvider\CustomizableOptionValue\Multiple</item>
3333
<item name="multiple" xsi:type="string">Magento\QuoteGraphQl\Model\CartItem\DataProvider\CustomizableOptionValue\Multiple</item>
34+
<item name="file" xsi:type="string">Magento\QuoteGraphQl\Model\CartItem\DataProvider\CustomizableOptionValue\File</item>
3435
</argument>
3536
</arguments>
3637
</type>
Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
<?php
2+
/************************************************************************
3+
*
4+
* ADOBE CONFIDENTIAL
5+
* ___________________
6+
*
7+
* Copyright 2024 Adobe
8+
* All Rights Reserved.
9+
*
10+
* NOTICE: All information contained herein is, and remains
11+
* the property of Adobe and its suppliers, if any. The intellectual
12+
* and technical concepts contained herein are proprietary to Adobe
13+
* and its suppliers and are protected by all applicable intellectual
14+
* property laws, including trade secret and copyright laws.
15+
* Dissemination of this information or reproduction of this material
16+
* is strictly forbidden unless prior written permission is obtained
17+
* from Adobe.
18+
* ************************************************************************
19+
*/
20+
declare(strict_types=1);
21+
22+
namespace Magento\GraphQl\Quote;
23+
24+
use Magento\Catalog\Api\Data\ProductCustomOptionInterface;
25+
use Magento\Catalog\Test\Fixture\Product;
26+
use Magento\Framework\App\Filesystem\DirectoryList;
27+
use Magento\Framework\DataObject;
28+
use Magento\Quote\Test\Fixture\GuestCart;
29+
use Magento\Quote\Test\Fixture\QuoteIdMask;
30+
use Magento\TestFramework\Fixture\DataFixture;
31+
use Magento\TestFramework\Fixture\DataFixtureStorageManager;
32+
use Magento\TestFramework\Helper\Bootstrap;
33+
use Magento\TestFramework\TestCase\GraphQlAbstract;
34+
use Magento\Quote\Api\CartRepositoryInterface;
35+
use Magento\Framework\Filesystem;
36+
use Magento\Framework\HTTP\Adapter\FileTransferFactory;
37+
use Laminas\File\Transfer\Adapter\Http;
38+
39+
#[
40+
DataFixture(GuestCart::class, as: 'quote'),
41+
DataFixture(QuoteIdMask::class, ['cart_id' => '$quote.id$'], 'quoteIdMask'),
42+
DataFixture(
43+
Product::class,
44+
[
45+
'options' => [
46+
[
47+
'title' => 'option title',
48+
'type' => ProductCustomOptionInterface::OPTION_TYPE_FILE,
49+
'file_extension' => 'jpg, png, gif',
50+
'values' => [
51+
[
52+
'title' => 'file',
53+
]
54+
]
55+
]
56+
]
57+
],
58+
'product'
59+
)
60+
]
61+
class GetCartQueryForProductWithCustomOptionFile extends GraphQlAbstract
62+
{
63+
/**
64+
* @var CartRepositoryInterface
65+
*/
66+
private $cartRepository;
67+
68+
/**
69+
* @var string
70+
*/
71+
private $fileToRemove;
72+
73+
/**
74+
* @inheritdoc
75+
*/
76+
protected function setUp(): void
77+
{
78+
$objectManager = Bootstrap::getObjectManager();
79+
$this->cartRepository = $objectManager->get(CartRepositoryInterface::class);
80+
}
81+
82+
/**
83+
* Test cart query
84+
*
85+
* @return void
86+
* @throws \Magento\Framework\Exception\LocalizedException
87+
*/
88+
public function testCartQueryForProductWithCustomOptionAsFile(): void
89+
{
90+
$cartId = DataFixtureStorageManager::getStorage()->get('quoteIdMask')->getMaskedId();
91+
$product = DataFixtureStorageManager::getStorage()->get('product');
92+
$this->addProductInQuote();
93+
$query = $this->getQuery($cartId);
94+
$response = $this->graphQlMutation($query);
95+
$items = $response['cart']['itemsV2']['items'];
96+
97+
$this->assertStringContainsString(
98+
'magento_small_image.jpg',
99+
$items[0]['customizable_options'][0]['values'][0]['value']
100+
);
101+
102+
$expectedResult = [
103+
'customizable_options'=>[[
104+
'type'=>'file',
105+
'label' => 'option title',
106+
'values'=>[
107+
[
108+
'label'=>'option title',
109+
'value' => $items[0]['customizable_options'][0]['values'][0]['value']
110+
]
111+
]
112+
]],
113+
'product' => ['name' => $product->getName(), 'sku'=>$product->getData('sku')],
114+
'quantity' => 1
115+
];
116+
117+
$this->assertResponseFields($expectedResult, $items[0]);
118+
}
119+
120+
/**
121+
* Add product in cart
122+
*
123+
* @return void
124+
* @throws \Magento\Framework\Exception\LocalizedException
125+
*/
126+
private function addProductInQuote(): void
127+
{
128+
$quote = DataFixtureStorageManager::getStorage()->get('quote');
129+
$product = DataFixtureStorageManager::getStorage()->get('product');
130+
$productOptions = $product->getOptions();
131+
$fileOptionId = [];
132+
foreach ($productOptions as $option) {
133+
foreach ($option->getValues() as $value) {
134+
$fileOptionId[] = $value['option_id'];
135+
}
136+
}
137+
138+
$this->prepareEnv($fileOptionId);
139+
$buyRequest = new DataObject(
140+
[
141+
'qty' => 1,
142+
'options' => ['files_prefix' => 'item_simple_with_custom_file_option_'],
143+
]
144+
);
145+
$quote->addProduct($product, $buyRequest);
146+
$this->cartRepository->save($quote);
147+
}
148+
149+
/**
150+
* Prepare file upload environment
151+
*
152+
* @param array $optionIds
153+
* @return void
154+
*/
155+
private function prepareEnv(array $optionIds): void
156+
{
157+
$file = 'magento_small_image.jpg';
158+
$fixtureDir = realpath(__DIR__ . '/../_files/');
159+
/** @var Filesystem $filesystem */
160+
$filesystem = Bootstrap::getObjectManager()->get(Filesystem::class);
161+
$tmpDirectory = $filesystem->getDirectoryWrite(DirectoryList::SYS_TMP);
162+
$filePath = $tmpDirectory->getAbsolutePath($file);
163+
$this->fileToRemove = $filePath;
164+
copy($fixtureDir . DIRECTORY_SEPARATOR . $file, $filePath);
165+
166+
$_FILES["item_simple_with_custom_file_option_options_$optionIds[0]_file"] = [
167+
'name' => 'test.jpg',
168+
'type' => 'image/jpeg',
169+
'tmp_name' => $filePath,
170+
'error' => 0,
171+
'size' => '3046',
172+
];
173+
$this->prepareUploaderFactoryMock();
174+
}
175+
176+
/**
177+
* Prepare file upload validator mock
178+
*
179+
* @return void
180+
*/
181+
private function prepareUploaderFactoryMock(): void
182+
{
183+
$uploaderMock = $this->getPreparedUploader();
184+
/** @var FileTransferFactory $httpFactory */
185+
$httpFactoryMock = $this->createPartialMock(FileTransferFactory::class, ['create']);
186+
$httpFactoryMock
187+
->method('create')
188+
->willReturnOnConsecutiveCalls($uploaderMock, clone $uploaderMock);
189+
Bootstrap::getObjectManager()->addSharedInstance($httpFactoryMock, FileTransferFactory::class);
190+
}
191+
192+
/**
193+
* @inheritdoc
194+
*/
195+
protected function tearDown(): void
196+
{
197+
if (file_exists($this->fileToRemove)) {
198+
unlink($this->fileToRemove);
199+
}
200+
201+
parent::tearDown();
202+
}
203+
204+
/**
205+
* Create prepared uploader instance for test
206+
*
207+
* @return Http
208+
*/
209+
private function getPreparedUploader(): Http
210+
{
211+
$uploader = new Http();
212+
$refObject = new \ReflectionObject($uploader);
213+
$validators = $refObject->getProperty('validators');
214+
$validators->setAccessible(true);
215+
$validators->setValue($uploader, []);
216+
$files = $refObject->getProperty('files');
217+
$files->setAccessible(true);
218+
$filesValues = $files->getValue($uploader);
219+
foreach (array_keys($filesValues) as $value) {
220+
$filesValues[$value]['validators'] = [];
221+
}
222+
$files->setValue($uploader, $filesValues);
223+
return $uploader;
224+
}
225+
226+
/**
227+
* Create cart query
228+
*
229+
* @param string $maskedQuoteId
230+
* @return string
231+
*/
232+
private function getQuery(string $maskedQuoteId): string
233+
{
234+
return <<<QUERY
235+
{
236+
cart(cart_id: "{$maskedQuoteId}") {
237+
itemsV2 {
238+
total_count
239+
items {
240+
... on SimpleCartItem {
241+
customizable_options {
242+
type
243+
label
244+
values {
245+
label
246+
value
247+
}
248+
}
249+
}
250+
product {
251+
name
252+
sku
253+
}
254+
quantity
255+
}
256+
}
257+
prices {
258+
grand_total {
259+
value
260+
currency
261+
}
262+
}
263+
}
264+
}
265+
QUERY;
266+
}
267+
}
Loading

0 commit comments

Comments
 (0)