Skip to content

Commit dbb7dec

Browse files
authored
Merge pull request #5433 from magento-honey-badgers/MC-31616
[honey] MC-31616: Forward-port 2.3.5 GraphQL bug fixes
2 parents 78bb169 + 301e4b6 commit dbb7dec

21 files changed

+636
-268
lines changed

app/code/Magento/CatalogGraphQl/Model/Config/FilterAttributeReader.php

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,15 @@
1010
use Magento\Framework\Config\ReaderInterface;
1111
use Magento\Framework\GraphQl\Schema\Type\Entity\MapperInterface;
1212
use Magento\Catalog\Model\ResourceModel\Product\Attribute\CollectionFactory;
13-
use Magento\Catalog\Model\ResourceModel\Product\Attribute\Collection;
1413
use Magento\Catalog\Model\ResourceModel\Eav\Attribute;
1514

1615
/**
1716
* Adds custom/eav attributes to product filter type in the GraphQL config.
1817
*
1918
* Product Attribute should satisfy the following criteria:
20-
* - Attribute is searchable
21-
* - "Visible in Advanced Search" is set to "Yes"
22-
* - Attribute of type "Select" must have options
19+
* - (Attribute is searchable AND "Visible in Advanced Search" is set to "Yes")
20+
* - OR attribute is "Used in Layered Navigation"
21+
* - AND Attribute of type "Select" must have options
2322
*/
2423
class FilterAttributeReader implements ReaderInterface
2524
{
@@ -77,7 +76,7 @@ public function read($scope = null) : array
7776
$typeNames = $this->mapper->getMappedTypes(self::ENTITY_TYPE);
7877
$config = [];
7978

80-
foreach ($this->getAttributeCollection() as $attribute) {
79+
foreach ($this->getFilterAttributes() as $attribute) {
8180
$attributeCode = $attribute->getAttributeCode();
8281

8382
foreach ($typeNames as $typeName) {
@@ -120,15 +119,25 @@ private function getFilterType(Attribute $attribute): string
120119
}
121120

122121
/**
123-
* Create attribute collection
122+
* Get attributes to use in product filter input
124123
*
125-
* @return Collection|\Magento\Catalog\Model\ResourceModel\Eav\Attribute[]
124+
* @return array
126125
*/
127-
private function getAttributeCollection()
126+
private function getFilterAttributes(): array
128127
{
129-
return $this->collectionFactory->create()
128+
$filterableAttributes = $this->collectionFactory
129+
->create()
130+
->addHasOptionsFilter()
131+
->addIsFilterableFilter()
132+
->getItems();
133+
134+
$searchableAttributes = $this->collectionFactory
135+
->create()
130136
->addHasOptionsFilter()
131137
->addIsSearchableFilter()
132-
->addDisplayInAdvancedSearchFilter();
138+
->addDisplayInAdvancedSearchFilter()
139+
->getItems();
140+
141+
return $filterableAttributes + $searchableAttributes;
133142
}
134143
}

app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Image.php

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
use Magento\Framework\Exception\LocalizedException;
1414
use Magento\Store\Api\Data\StoreInterface;
1515
use Magento\Framework\Filesystem\DirectoryList;
16+
use Magento\Catalog\Model\Category\FileInfo;
17+
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
1618

1719
/**
1820
* Resolve category image to a fully qualified URL
@@ -22,12 +24,19 @@ class Image implements ResolverInterface
2224
/** @var DirectoryList */
2325
private $directoryList;
2426

27+
/** @var FileInfo */
28+
private $fileInfo;
29+
2530
/**
2631
* @param DirectoryList $directoryList
32+
* @param FileInfo $fileInfo
2733
*/
28-
public function __construct(DirectoryList $directoryList)
29-
{
34+
public function __construct(
35+
DirectoryList $directoryList,
36+
FileInfo $fileInfo
37+
) {
3038
$this->directoryList = $directoryList;
39+
$this->fileInfo = $fileInfo;
3140
}
3241

3342
/**
@@ -45,21 +54,40 @@ public function resolve(
4554
}
4655
/** @var \Magento\Catalog\Model\Category $category */
4756
$category = $value['model'];
48-
$imagePath = $category->getImage();
57+
$imagePath = $category->getData('image');
4958
if (empty($imagePath)) {
5059
return null;
5160
}
5261
/** @var StoreInterface $store */
5362
$store = $context->getExtensionAttributes()->getStore();
54-
$baseUrl = $store->getBaseUrl('media');
63+
$baseUrl = $store->getBaseUrl();
5564

56-
$mediaPath = $this->directoryList->getUrlPath('media');
57-
$pos = strpos($imagePath, $mediaPath);
58-
if ($pos !== false) {
59-
$imagePath = substr($imagePath, $pos + strlen($mediaPath), strlen($baseUrl));
65+
$filenameWithMedia = $this->fileInfo->isBeginsWithMediaDirectoryPath($imagePath)
66+
? $imagePath : $this->formatFileNameWithMediaCategoryFolder($imagePath);
67+
68+
if (!$this->fileInfo->isExist($filenameWithMedia)) {
69+
throw new GraphQlInputException(__('Category image not found.'));
6070
}
61-
$imageUrl = rtrim($baseUrl, '/') . '/' . ltrim($imagePath, '/');
6271

63-
return $imageUrl;
72+
// return full url
73+
return rtrim($baseUrl, '/') . $filenameWithMedia;
74+
}
75+
76+
/**
77+
* Format category media folder to filename
78+
*
79+
* @param string $fileName
80+
* @return string
81+
*/
82+
private function formatFileNameWithMediaCategoryFolder(string $fileName): string
83+
{
84+
// phpcs:ignore Magento2.Functions.DiscouragedFunction
85+
$baseFileName = basename($fileName);
86+
return '/'
87+
. $this->directoryList->getUrlPath('media')
88+
. '/'
89+
. ltrim(FileInfo::ENTITY_MEDIA_PATH, '/')
90+
. '/'
91+
. $baseFileName;
6492
}
6593
}

app/code/Magento/CatalogGraphQl/Model/Resolver/Products/FilterArgument/ProductEntityAttributesForAst.php

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@
1717
*/
1818
class ProductEntityAttributesForAst implements FieldEntityAttributesInterface
1919
{
20+
private const PRODUCT_BASE_TYPE = 'SimpleProduct';
21+
22+
private const PRODUCT_FILTER_INPUT = 'ProductAttributeFilterInput';
23+
2024
/**
2125
* @var ConfigInterface
2226
*/
@@ -51,9 +55,9 @@ public function __construct(
5155
*/
5256
public function getEntityAttributes() : array
5357
{
54-
$productTypeSchema = $this->config->getConfigElement('SimpleProduct');
58+
$productTypeSchema = $this->config->getConfigElement(self::PRODUCT_BASE_TYPE);
5559
if (!$productTypeSchema instanceof Type) {
56-
throw new \LogicException(__("SimpleProduct type not defined in schema."));
60+
throw new \LogicException(__("%1 type not defined in schema.", self::PRODUCT_BASE_TYPE));
5761
}
5862

5963
$fields = [];
@@ -69,6 +73,9 @@ public function getEntityAttributes() : array
6973
}
7074
}
7175

76+
$productAttributeFilterFields = $this->getProductAttributeFilterFields();
77+
$fields = array_merge($fields, $productAttributeFilterFields);
78+
7279
foreach ($this->additionalAttributes as $attributeName) {
7380
$fields[$attributeName] = [
7481
'type' => 'String',
@@ -78,4 +85,24 @@ public function getEntityAttributes() : array
7885

7986
return $fields;
8087
}
88+
89+
/**
90+
* Get fields from ProductAttributeFilterInput
91+
*
92+
* @return array
93+
*/
94+
private function getProductAttributeFilterFields()
95+
{
96+
$filterFields = [];
97+
98+
$productAttributeFilterSchema = $this->config->getConfigElement(self::PRODUCT_FILTER_INPUT);
99+
$productAttributeFilterFields = $productAttributeFilterSchema->getFields();
100+
foreach ($productAttributeFilterFields as $filterField) {
101+
$filterFields[$filterField->getName()] = [
102+
'type' => 'String',
103+
'fieldName' => $filterField->getName(),
104+
];
105+
}
106+
return $filterFields;
107+
}
81108
}

app/code/Magento/CatalogGraphQl/etc/schema.graphqls

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -199,11 +199,17 @@ type CustomizableFileValue @doc(description: "CustomizableFileValue defines the
199199
interface MediaGalleryInterface @doc(description: "Contains basic information about a product image or video.") @typeResolver(class: "Magento\\CatalogGraphQl\\Model\\MediaGalleryTypeResolver") {
200200
url: String @doc(description: "The URL of the product image or video.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\MediaGallery\\Url")
201201
label: String @doc(description: "The label of the product image or video.")
202+
position: Int @doc(description: "The media item's position after it has been sorted.")
203+
disabled: Boolean @doc(description: "Whether the image is hidden from view.")
202204
}
203205

204206
type ProductImage implements MediaGalleryInterface @doc(description: "Product image information. Contains the image URL and label.") {
205207
}
206208

209+
type ProductVideo implements MediaGalleryInterface @doc(description: "Contains information about a product video.") {
210+
video_content: ProductMediaGalleryEntriesVideoContent @doc(description: "Contains a ProductMediaGalleryEntriesVideoContent object.")
211+
}
212+
207213
interface CustomizableOptionInterface @typeResolver(class: "Magento\\CatalogGraphQl\\Model\\CustomizableOptionTypeResolver") @doc(description: "The CustomizableOptionInterface contains basic information about a customizable option. It can be implemented by several types of configurable options.") {
208214
title: String @doc(description: "The display name for this option.")
209215
required: Boolean @doc(description: "Indicates whether the option is required.")
@@ -403,7 +409,7 @@ type MediaGalleryEntry @doc(description: "MediaGalleryEntry defines characterist
403409
media_type: String @doc(description: "image or video.")
404410
label: String @doc(description: "The alt text displayed on the UI when the user points to the image.")
405411
position: Int @doc(description: "The media item's position after it has been sorted.")
406-
disabled: Boolean @doc(description: "Whether the image is hidden from vie.")
412+
disabled: Boolean @doc(description: "Whether the image is hidden from view.")
407413
types: [String] @doc(description: "Array of image types. It can have the following values: image, small_image, thumbnail.")
408414
file: String @doc(description: "The path of the image on the server.")
409415
content: ProductMediaGalleryEntriesContent @doc(description: "Contains a ProductMediaGalleryEntriesContent object.")
@@ -466,7 +472,3 @@ type StoreConfig @doc(description: "The type contains information about a store
466472
catalog_default_sort_by : String @doc(description: "Default Sort By.")
467473
root_category_id: Int @doc(description: "The ID of the root category") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\RootCategoryId")
468474
}
469-
470-
type ProductVideo @doc(description: "Contains information about a product video.") implements MediaGalleryInterface {
471-
video_content: ProductMediaGalleryEntriesVideoContent @doc(description: "Contains a ProductMediaGalleryEntriesVideoContent object.")
472-
}

app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/AvailableShippingMethods.php

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,14 @@
77

88
namespace Magento\QuoteGraphQl\Model\Resolver\ShippingAddress;
99

10-
use Magento\Directory\Model\Currency;
1110
use Magento\Framework\Api\ExtensibleDataObjectConverter;
1211
use Magento\Framework\Exception\LocalizedException;
13-
use Magento\Framework\Exception\NoSuchEntityException;
1412
use Magento\Framework\GraphQl\Config\Element\Field;
1513
use Magento\Framework\GraphQl\Query\ResolverInterface;
1614
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
1715
use Magento\Quote\Api\Data\ShippingMethodInterface;
1816
use Magento\Quote\Model\Cart\ShippingMethodConverter;
19-
use Magento\Store\Api\Data\StoreInterface;
17+
use Magento\Quote\Model\Quote\TotalsCollector;
2018

2119
/**
2220
* @inheritdoc
@@ -33,16 +31,24 @@ class AvailableShippingMethods implements ResolverInterface
3331
*/
3432
private $shippingMethodConverter;
3533

34+
/**
35+
* @var TotalsCollector
36+
*/
37+
private $totalsCollector;
38+
3639
/**
3740
* @param ExtensibleDataObjectConverter $dataObjectConverter
3841
* @param ShippingMethodConverter $shippingMethodConverter
42+
* @param TotalsCollector $totalsCollector
3943
*/
4044
public function __construct(
4145
ExtensibleDataObjectConverter $dataObjectConverter,
42-
ShippingMethodConverter $shippingMethodConverter
46+
ShippingMethodConverter $shippingMethodConverter,
47+
TotalsCollector $totalsCollector
4348
) {
4449
$this->dataObjectConverter = $dataObjectConverter;
4550
$this->shippingMethodConverter = $shippingMethodConverter;
51+
$this->totalsCollector = $totalsCollector;
4652
}
4753

4854
/**
@@ -61,9 +67,8 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value
6167
}
6268

6369
$address->setCollectShippingRates(true);
64-
$address->collectShippingRates();
6570
$cart = $address->getQuote();
66-
71+
$this->totalsCollector->collectAddressTotals($cart, $address);
6772
$methods = [];
6873
$shippingRates = $address->getGroupedAllShippingRates();
6974
foreach ($shippingRates as $carrierRates) {
@@ -88,7 +93,6 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value
8893
* @param array $data
8994
* @param string $quoteCurrencyCode
9095
* @return array
91-
* @throws NoSuchEntityException
9296
*/
9397
private function processMoneyTypeData(array $data, string $quoteCurrencyCode): array
9498
{

dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryListTest.php

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
namespace Magento\GraphQl\Catalog;
99

10+
use Magento\Catalog\Model\ResourceModel\Category\Collection as CategoryCollection;
11+
use Magento\Framework\ObjectManagerInterface;
1012
use Magento\Store\Model\StoreManagerInterface;
1113
use Magento\TestFramework\Helper\Bootstrap;
1214
use Magento\TestFramework\TestCase\GraphQlAbstract;
@@ -16,6 +18,16 @@
1618
*/
1719
class CategoryListTest extends GraphQlAbstract
1820
{
21+
/**
22+
* @var ObjectManagerInterface
23+
*/
24+
private $objectManager;
25+
26+
protected function setUp()
27+
{
28+
$this->objectManager = Bootstrap::getObjectManager();
29+
}
30+
1931
/**
2032
* @magentoApiDataFixture Magento/Catalog/_files/categories.php
2133
* @dataProvider filterSingleCategoryDataProvider
@@ -333,7 +345,7 @@ public function testEmptyFiltersReturnRootCategory()
333345
}
334346
}
335347
QUERY;
336-
$storeManager = Bootstrap::getObjectManager()->get(StoreManagerInterface::class);
348+
$storeManager = $this->objectManager->get(StoreManagerInterface::class);
337349
$storeRootCategoryId = $storeManager->getStore()->getRootCategoryId();
338350

339351
$result = $this->graphQlQuery($query);
@@ -369,6 +381,46 @@ public function testMinimumMatchQueryLength()
369381
$this->assertEquals([], $result['categoryList']);
370382
}
371383

384+
/**
385+
* Test category image full name is returned
386+
*
387+
* @magentoApiDataFixture Magento/Catalog/_files/catalog_category_with_long_image_name.php
388+
*/
389+
public function testCategoryImageName()
390+
{
391+
/** @var CategoryCollection $categoryCollection */
392+
$categoryCollection = $this->objectManager->get(CategoryCollection::class);
393+
$categoryModel = $categoryCollection
394+
->addAttributeToSelect('image')
395+
->addAttributeToFilter('name', ['eq' => 'Parent Image Category'])
396+
->getFirstItem();
397+
$categoryId = $categoryModel->getId();
398+
399+
$query = <<<QUERY
400+
{
401+
categoryList(filters: {ids: {in: ["$categoryId"]}}) {
402+
id
403+
name
404+
image
405+
}
406+
}
407+
QUERY;
408+
$storeManager = $this->objectManager->get(StoreManagerInterface::class);
409+
$storeBaseUrl = $storeManager->getStore()->getBaseUrl('media');
410+
411+
$expected = "catalog/category/magento_long_image_name_magento_long_image_name_magento_long_image_name.jpg";
412+
$expectedImageUrl = rtrim($storeBaseUrl, '/') . '/' . $expected;
413+
414+
$response = $this->graphQlQuery($query);
415+
$categoryList = $response['categoryList'];
416+
$this->assertArrayNotHasKey('errors', $response);
417+
$this->assertNotEmpty($response['categoryList']);
418+
$expectedImageUrl = str_replace('index.php/', '', $expectedImageUrl);
419+
$categoryList[0]['image'] = str_replace('index.php/', '', $categoryList[0]['image']);
420+
$this->assertEquals('Parent Image Category', $categoryList[0]['name']);
421+
$this->assertEquals($expectedImageUrl, $categoryList[0]['image']);
422+
}
423+
372424
/**
373425
* @return array
374426
*/

0 commit comments

Comments
 (0)