Skip to content

Commit fe13306

Browse files
committed
Merge remote-tracking branch 'mainline/2.4-develop' into MFTF-3.0.0
2 parents 6a2e0af + dbb7dec commit fe13306

25 files changed

+642
-269
lines changed

app/code/Magento/Catalog/Test/Mftf/Test/VerifyCategoryProductAndProductCategoryPartialReindexTest.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757

5858
<wait stepKey="waitBeforeRunCronIndex" time="30"/>
5959
<magentoCLI stepKey="runCronIndex" command="cron:run --group=index"/>
60+
<wait stepKey="waitAfterRunCronIndex" time="60"/>
6061
</before>
6162
<after>
6263
<!-- Change "Category Products" and "Product Categories" indexers to "Update on Save" mode -->
@@ -141,6 +142,7 @@
141142
<!-- Run cron -->
142143
<wait stepKey="waitBeforeRunMagentoCron" time="30"/>
143144
<magentoCLI stepKey="runMagentoCron" command="cron:run --group=index"/>
145+
<wait stepKey="waitAfterRunMagentoCron" time="60"/>
144146

145147
<!-- Open categories K, L, M, N on Storefront in order to make sure that new assigments are applied -->
146148
<!-- Category K contains only Products A, C -->
@@ -202,6 +204,7 @@
202204
<!-- Run Cron once to reindex product changes -->
203205
<wait stepKey="waitBeforeRunCronIndexAfterProductAssignToCategory" time="30"/>
204206
<magentoCLI stepKey="runCronIndexAfterProductAssignToCategory" command="cron:run --group=index"/>
207+
<wait stepKey="waitAfterRunCronIndexAfterProductAssignToCategory" time="60"/>
205208

206209
<!-- Open categories K, L, M, N on Storefront in order to make sure that new assigments are applied -->
207210

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/CatalogImportExport/Test/Mftf/Test/AdminExportImportConfigurableProductWithImagesTest.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,6 @@
134134
<magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove example.com static.magento.com"/>
135135

136136
<!-- Delete created data -->
137-
<deleteData createDataKey="createCategory" stepKey="deleteCategory"/>
138137
<deleteData createDataKey="createFirstSimpleProduct" stepKey="deleteFisrtSimpleProduct"/>
139138
<deleteData createDataKey="createSecondSimpleProduct" stepKey="deleteSecondSimpleProduct"/>
140139
<deleteData createDataKey="createDownloadableProduct" stepKey="deleteDownloadableProduct"/>
@@ -147,6 +146,7 @@
147146
<deleteData createDataKey="createConfigurableProduct" stepKey="deleteConfigurableProduct"/>
148147
<deleteData createDataKey="createConfigChildProduct" stepKey="deleteConfigChildProduct"/>
149148
<deleteData createDataKey="createConfigProductAttr" stepKey="deleteConfigProductAttr"/>
149+
<deleteData createDataKey="createCategory" stepKey="deleteCategory"/>
150150
<amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/>
151151
<actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGridColumnsInitial"/>
152152
<!-- Admin logout-->

app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminCatalogPriceRuleSaveAndApplyActionGroup.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
</annotations>
1414

1515
<scrollToTopOfPage stepKey="scrollToTop"/>
16+
<waitForElementVisible selector="{{AdminNewCatalogPriceRule.saveAndApply}}" stepKey="waitForSaveAndApplyButton"/>
1617
<click selector="{{AdminNewCatalogPriceRule.saveAndApply}}" stepKey="saveAndApplyRule"/>
1718
<waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="waitForSuccessMessageAppears"/>
1819
<see selector="{{AdminMessagesSection.success}}" userInput="You saved the rule." stepKey="checkSuccessSaveMessage"/>

app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithLinkTest.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969

7070
<!-- Save product -->
7171
<actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/>
72+
<magentoCron stepKey="runIndexCronJobs" groups="index"/>
7273

7374
<!-- Assert product in storefront category page -->
7475
<amOnPage url="$$createCategory.name$$.html" stepKey="amOnCategoryPage"/>

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
{

0 commit comments

Comments
 (0)