From baa17d79f94f4375a31c593ec757e8c7be2a70c6 Mon Sep 17 00:00:00 2001 From: Alex Taranovsky Date: Thu, 19 Mar 2020 02:40:12 +0200 Subject: [PATCH 01/28] =?UTF-8?q?magento/magento2#:=20GraphQl.=20Remove=20?= =?UTF-8?q?a=20redundant=20logic=20in=20=E2=80=9CSetShippingMethodsOnCart?= =?UTF-8?q?=E2=80=9D=20mutation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../QuoteGraphQl/Model/Cart/SetShippingMethodsOnCart.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingMethodsOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingMethodsOnCart.php index b2526bdc04e98..654a4bb558632 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingMethodsOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingMethodsOnCart.php @@ -42,12 +42,12 @@ public function execute(ContextInterface $context, CartInterface $cart, array $s } $shippingMethodInput = current($shippingMethodsInput); - if (!isset($shippingMethodInput['carrier_code']) || empty($shippingMethodInput['carrier_code'])) { + if (empty($shippingMethodInput['carrier_code'])) { throw new GraphQlInputException(__('Required parameter "carrier_code" is missing.')); } $carrierCode = $shippingMethodInput['carrier_code']; - if (!isset($shippingMethodInput['method_code']) || empty($shippingMethodInput['method_code'])) { + if (empty($shippingMethodInput['method_code'])) { throw new GraphQlInputException(__('Required parameter "method_code" is missing.')); } $methodCode = $shippingMethodInput['method_code']; From e5e50e7767e7d87f145e3f4fc06dacef95906354 Mon Sep 17 00:00:00 2001 From: Alex Taranovsky Date: Wed, 3 Jun 2020 13:44:37 +0300 Subject: [PATCH 02/28] magento/magento2#28481: GraphQl. updateCustomer allows to set any INT value in gender argument --- .../Model/Customer/ValidateCustomerData.php | 70 +++++++++++++++++-- .../GraphQl/Customer/UpdateCustomerTest.php | 48 ++++++++++--- 2 files changed, 105 insertions(+), 13 deletions(-) diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/ValidateCustomerData.php b/app/code/Magento/CustomerGraphQl/Model/Customer/ValidateCustomerData.php index 3861ce324ea7d..5d60df5cba4d3 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Customer/ValidateCustomerData.php +++ b/app/code/Magento/CustomerGraphQl/Model/Customer/ValidateCustomerData.php @@ -7,6 +7,10 @@ namespace Magento\CustomerGraphQl\Model\Customer; +use Magento\Customer\Api\CustomerMetadataInterface; +use Magento\Customer\Model\Data\AttributeMetadata; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\Validator\EmailAddress as EmailAddressValidator; @@ -15,6 +19,11 @@ */ class ValidateCustomerData { + /** + * @var CustomerMetadataInterface + */ + private $customerMetadata; + /** * Get allowed/required customer attributes * @@ -32,25 +41,40 @@ class ValidateCustomerData * * @param GetAllowedCustomerAttributes $getAllowedCustomerAttributes * @param EmailAddressValidator $emailAddressValidator + * @param CustomerMetadataInterface $customerMetadata */ public function __construct( GetAllowedCustomerAttributes $getAllowedCustomerAttributes, - EmailAddressValidator $emailAddressValidator + EmailAddressValidator $emailAddressValidator, + CustomerMetadataInterface $customerMetadata ) { $this->getAllowedCustomerAttributes = $getAllowedCustomerAttributes; $this->emailAddressValidator = $emailAddressValidator; + $this->customerMetadata = $customerMetadata; } /** * Validate customer data * * @param array $customerData - * - * @return void - * * @throws GraphQlInputException + * @throws LocalizedException + * @throws NoSuchEntityException */ public function execute(array $customerData): void + { + $this->validateRequiredArguments($customerData); + $this->validateEmail($customerData); + $this->validateGender($customerData); + } + + /** + * Validate required attributes + * + * @param array $customerData + * @throws GraphQlInputException + */ + private function validateRequiredArguments(array $customerData): void { $attributes = $this->getAllowedCustomerAttributes->execute(array_keys($customerData)); $errorInput = []; @@ -69,11 +93,49 @@ public function execute(array $customerData): void __('Required parameters are missing: %1', [implode(', ', $errorInput)]) ); } + } + /** + * Validate an email + * + * @param array $customerData + * @throws GraphQlInputException + */ + private function validateEmail(array $customerData): void + { if (isset($customerData['email']) && !$this->emailAddressValidator->isValid($customerData['email'])) { throw new GraphQlInputException( __('"%1" is not a valid email address.', $customerData['email']) ); } } + + /** + * Validate gender value + * + * @param array $customerData + * @throws GraphQlInputException + * @throws LocalizedException + * @throws NoSuchEntityException + */ + private function validateGender(array $customerData): void + { + if (isset($customerData['gender']) && $customerData['gender']) { + /** @var AttributeMetadata $genderData */ + $options = $this->customerMetadata->getAttributeMetadata('gender')->getOptions(); + + $isValid = false; + foreach ($options as $optionData) { + if ($optionData->getValue() && $optionData->getValue() == $customerData['gender']) { + $isValid = true; + } + } + + if (!$isValid) { + throw new GraphQlInputException( + __('"%1" is not a valid gender value.', $customerData['gender']) + ); + } + } + } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/UpdateCustomerTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/UpdateCustomerTest.php index 6e90e85782bb2..49595562a6c56 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/UpdateCustomerTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/UpdateCustomerTest.php @@ -7,8 +7,9 @@ namespace Magento\GraphQl\Customer; +use Exception; use Magento\Customer\Model\CustomerAuthUpdate; -use Magento\Customer\Model\CustomerRegistry; +use Magento\Framework\Exception\AuthenticationException; use Magento\Integration\Api\CustomerTokenServiceInterface; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\TestCase\GraphQlAbstract; @@ -113,7 +114,7 @@ public function testUpdateCustomer() */ public function testUpdateCustomerIfInputDataIsEmpty() { - $this->expectException(\Exception::class); + $this->expectException(Exception::class); $this->expectExceptionMessage('"input" value should be specified'); $currentEmail = 'customer@example.com'; @@ -139,7 +140,7 @@ public function testUpdateCustomerIfInputDataIsEmpty() */ public function testUpdateCustomerIfUserIsNotAuthorized() { - $this->expectException(\Exception::class); + $this->expectException(Exception::class); $this->expectExceptionMessage('The current customer isn\'t authorized.'); $newFirstname = 'Richard'; @@ -165,7 +166,7 @@ public function testUpdateCustomerIfUserIsNotAuthorized() */ public function testUpdateCustomerIfAccountIsLocked() { - $this->expectException(\Exception::class); + $this->expectException(Exception::class); $this->expectExceptionMessage('The account is locked.'); $this->lockCustomer->execute(1); @@ -195,7 +196,7 @@ public function testUpdateCustomerIfAccountIsLocked() */ public function testUpdateEmailIfPasswordIsMissed() { - $this->expectException(\Exception::class); + $this->expectException(Exception::class); $this->expectExceptionMessage('Provide the current "password" to change "email".'); $currentEmail = 'customer@example.com'; @@ -223,7 +224,7 @@ public function testUpdateEmailIfPasswordIsMissed() */ public function testUpdateEmailIfPasswordIsInvalid() { - $this->expectException(\Exception::class); + $this->expectException(Exception::class); $this->expectExceptionMessage('Invalid login or password.'); $currentEmail = 'customer@example.com'; @@ -253,8 +254,10 @@ public function testUpdateEmailIfPasswordIsInvalid() */ public function testUpdateEmailIfEmailAlreadyExists() { - $this->expectException(\Exception::class); - $this->expectExceptionMessage('A customer with the same email address already exists in an associated website.'); + $this->expectException(Exception::class); + $this->expectExceptionMessage( + 'A customer with the same email address already exists in an associated website.' + ); $currentEmail = 'customer@example.com'; $currentPassword = 'password'; @@ -286,7 +289,7 @@ public function testUpdateEmailIfEmailAlreadyExists() */ public function testEmptyCustomerName() { - $this->expectException(\Exception::class); + $this->expectException(Exception::class); $this->expectExceptionMessage('Required parameters are missing: First Name'); $currentEmail = 'customer@example.com'; @@ -310,10 +313,37 @@ public function testEmptyCustomerName() $this->graphQlMutation($query, [], '', $this->getCustomerAuthHeaders($currentEmail, $currentPassword)); } + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + */ + public function testUpdateCustomerWithIncorrectGender() + { + $gender = 5; + + $this->expectException(Exception::class); + $this->expectExceptionMessage('"' . $gender . '" is not a valid gender value.'); + + $query = <<graphQlMutation($query, [], '', $this->getCustomerAuthHeaders('customer@example.com', 'password')); + } + /** * @param string $email * @param string $password * @return array + * @throws AuthenticationException */ private function getCustomerAuthHeaders(string $email, string $password): array { From 0d356ac334ef4aa8b891b8467954d8717be988f8 Mon Sep 17 00:00:00 2001 From: Alex Taranovsky Date: Wed, 3 Jun 2020 17:13:49 +0300 Subject: [PATCH 03/28] magento/magento2#: GraphQl. setShippingAddressesOnCart. Test coverage for `The shipping address must contain either "customer_address_id" or "address".` error. --- .../Model/Cart/SetShippingAddressesOnCart.php | 5 +- .../Customer/SetShippingAddressOnCartTest.php | 51 +++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php index f73daa715c1df..e959c19a7cbe4 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php @@ -51,7 +51,10 @@ public function execute(ContextInterface $context, CartInterface $cart, array $s $shippingAddressInput = current($shippingAddressesInput) ?? []; $customerAddressId = $shippingAddressInput['customer_address_id'] ?? null; - if (!$customerAddressId && !isset($shippingAddressInput['address']['save_in_address_book'])) { + if (!$customerAddressId + && isset($shippingAddressInput['address']) + && !isset($shippingAddressInput['address']['save_in_address_book']) + ) { $shippingAddressInput['address']['save_in_address_book'] = true; } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php index 3e06b89c77fb7..900a2877e8c7b 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php @@ -1745,6 +1745,57 @@ public function testSetNewShippingAddressWithDefaultValueOfSaveInAddressBookAndP } } + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + */ + public function testSetShippingAddressOnCartWithNullCustomerAddressId() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + + $query = <<expectExceptionMessage( + 'The shipping address must contain either "customer_address_id" or "address".' + ); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); + } + /** * Verify the all the whitelisted fields for a New Address Object * From 516f9928bd02b51f24264c8716ad0f606f989087 Mon Sep 17 00:00:00 2001 From: Alex Taranovsky Date: Wed, 3 Jun 2020 23:58:27 +0300 Subject: [PATCH 04/28] magento/magento2#: GraphQl. setPaymentMethodAndPlaceOrder. Remove redundant logic. --- .../Resolver/SetPaymentAndPlaceOrder.php | 7 +-- .../SetPaymentMethodAndPlaceOrderTest.php | 44 +++++++++++++++++++ .../SetPaymentMethodAndPlaceOrderTest.php | 44 +++++++++++++++++++ 3 files changed, 92 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/SetPaymentAndPlaceOrder.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetPaymentAndPlaceOrder.php index dd4ce8fe7f7a6..c2e4bfa44c9bb 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/SetPaymentAndPlaceOrder.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetPaymentAndPlaceOrder.php @@ -71,14 +71,15 @@ public function __construct( */ public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) { - if (!isset($args['input']['cart_id']) || empty($args['input']['cart_id'])) { + if (empty($args['input']['cart_id'])) { throw new GraphQlInputException(__('Required parameter "cart_id" is missing')); } - $maskedCartId = $args['input']['cart_id']; - if (!isset($args['input']['payment_method']['code']) || empty($args['input']['payment_method']['code'])) { + if (empty($args['input']['payment_method']['code'])) { throw new GraphQlInputException(__('Required parameter "code" for "payment_method" is missing.')); } + + $maskedCartId = $args['input']['cart_id']; $paymentData = $args['input']['payment_method']; $storeId = (int)$context->getExtensionAttributes()->getStore()->getId(); diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetPaymentMethodAndPlaceOrderTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetPaymentMethodAndPlaceOrderTest.php index 21a8d6ae94312..ff8d4f4280c10 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetPaymentMethodAndPlaceOrderTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetPaymentMethodAndPlaceOrderTest.php @@ -281,6 +281,50 @@ public function testSetDisabledPaymentOnCart() $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_billing_address.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_flatrate_shipping_method.php + */ + public function testPlaceOrderWitMissingCartId() + { + $methodCode = Checkmo::PAYMENT_METHOD_CHECKMO_CODE; + $maskedQuoteId = ""; + + $query = $this->getQuery($maskedQuoteId, $methodCode); + + $this->expectExceptionMessage( + "Required parameter \"cart_id\" is missing" + ); + $this->graphQlMutation($query); + } + + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_billing_address.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_flatrate_shipping_method.php + */ + public function testPlaceOrderWithMissingPaymentMethod() + { + $methodCode = ""; + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + + $query = $this->getQuery($maskedQuoteId, $methodCode); + + $this->expectExceptionMessage( + "Required parameter \"code\" for \"payment_method\" is missing." + ); + $this->graphQlMutation($query); + } + /** * @param string $maskedQuoteId * @param string $methodCode diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetPaymentMethodAndPlaceOrderTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetPaymentMethodAndPlaceOrderTest.php index dbc10700794fa..78691d8cbd889 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetPaymentMethodAndPlaceOrderTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetPaymentMethodAndPlaceOrderTest.php @@ -218,6 +218,50 @@ public function testSetDisabledPaymentOnCart() $this->graphQlMutation($query); } + /** + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/set_guest_email.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_billing_address.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_flatrate_shipping_method.php + */ + public function testPlaceOrderWitMissingCartId() + { + $methodCode = Checkmo::PAYMENT_METHOD_CHECKMO_CODE; + $maskedQuoteId = ""; + + $query = $this->getQuery($maskedQuoteId, $methodCode); + + $this->expectExceptionMessage( + "Required parameter \"cart_id\" is missing" + ); + $this->graphQlMutation($query); + } + + /** + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/set_guest_email.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_billing_address.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_flatrate_shipping_method.php + */ + public function testPlaceOrderWithMissingPaymentMethod() + { + $methodCode = ""; + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + + $query = $this->getQuery($maskedQuoteId, $methodCode); + + $this->expectExceptionMessage( + "Required parameter \"code\" for \"payment_method\" is missing." + ); + $this->graphQlMutation($query); + } + /** * @param string $maskedQuoteId * @param string $methodCode From 0010f64d70f0c70e82d932ee29eeab0d5fbf13b6 Mon Sep 17 00:00:00 2001 From: Dmitriy Gallyamov Date: Thu, 11 Jun 2020 18:01:18 +0300 Subject: [PATCH 05/28] magento/magento2#28628: GraphQL price range numeric values - Removed wildcard usage for price filters --- .../Elasticsearch/SearchAdapter/Dynamic/DataProvider.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/Dynamic/DataProvider.php b/app/code/Magento/Elasticsearch/SearchAdapter/Dynamic/DataProvider.php index 496a77e4c5ac3..7bc64b59ffe78 100644 --- a/app/code/Magento/Elasticsearch/SearchAdapter/Dynamic/DataProvider.php +++ b/app/code/Magento/Elasticsearch/SearchAdapter/Dynamic/DataProvider.php @@ -235,11 +235,9 @@ public function prepareData($range, array $dbRanges) { $data = []; if (!empty($dbRanges)) { - $lastIndex = array_keys($dbRanges); - $lastIndex = $lastIndex[count($lastIndex) - 1]; foreach ($dbRanges as $index => $count) { - $fromPrice = $index == 1 ? '' : ($index - 1) * $range; - $toPrice = $index == $lastIndex ? '' : $index * $range; + $fromPrice = $index == 1 ? 0 : ($index - 1) * $range; + $toPrice = $index * $range; $data[] = [ 'from' => $fromPrice, 'to' => $toPrice, From 4511c460ab78e751221e9a4ed57942740c9382d1 Mon Sep 17 00:00:00 2001 From: Munkh-Ulzii Balidar Date: Fri, 12 Jun 2020 17:50:51 +0200 Subject: [PATCH 06/28] 28584 fixed issue category query does not handle fragments 28584 fix typo 28584 fix typo 28584 implement fragment spread for category --- .../CatalogGraphQl/Model/AttributesJoiner.php | 60 ++++++++++++++++++- .../Model/Category/DepthCalculator.php | 41 ++++++++++++- .../Model/Resolver/Categories.php | 11 ++-- .../Product/ProductFieldsSelector.php | 5 +- .../Products/DataProvider/CategoryTree.php | 30 +++++++--- .../Products/DataProvider/ProductSearch.php | 2 +- .../GraphQl/Catalog/CategoryListTest.php | 56 +++++++++++++++++ 7 files changed, 182 insertions(+), 23 deletions(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/AttributesJoiner.php b/app/code/Magento/CatalogGraphQl/Model/AttributesJoiner.php index 69592657241a0..ee00cdeb52c30 100644 --- a/app/code/Magento/CatalogGraphQl/Model/AttributesJoiner.php +++ b/app/code/Magento/CatalogGraphQl/Model/AttributesJoiner.php @@ -8,7 +8,11 @@ namespace Magento\CatalogGraphQl\Model; use GraphQL\Language\AST\FieldNode; +use GraphQL\Language\AST\FragmentSpreadNode; +use GraphQL\Language\AST\InlineFragmentNode; +use GraphQL\Language\AST\NodeKind; use Magento\Eav\Model\Entity\Collection\AbstractCollection; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; /** * Joins attributes for provided field node field names. @@ -30,6 +34,11 @@ class AttributesJoiner */ private $fieldToAttributeMap = []; + /** + * @var ResolveInfo + */ + private $resolverInfo = null; + /** * @param array $fieldToAttributeMap */ @@ -65,10 +74,21 @@ public function getQueryFields(FieldNode $fieldNode): array $selectedFields = []; /** @var FieldNode $field */ foreach ($query as $field) { - if ($field->kind === 'InlineFragment') { - continue; + if ($field->kind === NodeKind::INLINE_FRAGMENT) { + $inlineFragmentFields = $this->addInlineFragmentFields($field); + $selectedFields = array_merge($selectedFields, $inlineFragmentFields); + } elseif ($field->kind === NodeKind::FRAGMENT_SPREAD && isset($this->resolverInfo->fragments[$field->name->value])) { + foreach ($this->resolverInfo->fragments[$field->name->value]->selectionSet->selections as $spreadNode) { + if (isset($spreadNode->selectionSet->selections)) { + $fragmentSpreadFields = $this->getQueryFields($spreadNode); + $selectedFields = array_merge($selectedFields, $fragmentSpreadFields); + } else { + $selectedFields[] = $spreadNode->name->value; + } + } + } else { + $selectedFields[] = $field->name->value; } - $selectedFields[] = $field->name->value; } $this->setSelectionsForFieldNode($fieldNode, $selectedFields); } @@ -76,6 +96,32 @@ public function getQueryFields(FieldNode $fieldNode): array return $this->getFieldNodeSelections($fieldNode); } + /** + * Add fields from inline fragment nodes + * + * @param InlineFragmentNode $inlineFragmentField + * @param array $inlineFragmentFields + * @return string[] + */ + private function addInlineFragmentFields(InlineFragmentNode $inlineFragmentField, $inlineFragmentFields = []) + { + $query = $inlineFragmentField->selectionSet->selections; + /** @var FieldNode $field */ + foreach ($query as $field) { + if ($field->kind === NodeKind::INLINE_FRAGMENT) { + $this->addInlineFragmentFields($field, $inlineFragmentFields); + } elseif (isset($field->selectionSet->selections)) { + if (is_array($queryFields = $this->getQueryFields($field))) { + $inlineFragmentFields = array_merge($inlineFragmentFields, $queryFields); + } + } else { + $inlineFragmentFields[] = $field->name->value; + } + } + + return array_unique($inlineFragmentFields); + } + /** * Add field to collection select * @@ -124,4 +170,12 @@ private function setSelectionsForFieldNode(FieldNode $fieldNode, array $selected { $this->queryFields[$fieldNode->name->value][$fieldNode->name->loc->start] = $selectedFields; } + + /** + * @param ResolveInfo $resolverInfo + */ + public function setResolverInfo(ResolveInfo $resolverInfo): void + { + $this->resolverInfo = $resolverInfo; + } } diff --git a/app/code/Magento/CatalogGraphQl/Model/Category/DepthCalculator.php b/app/code/Magento/CatalogGraphQl/Model/Category/DepthCalculator.php index b5d02511da4e7..6bbd163436c2c 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Category/DepthCalculator.php +++ b/app/code/Magento/CatalogGraphQl/Model/Category/DepthCalculator.php @@ -8,6 +8,9 @@ namespace Magento\CatalogGraphQl\Model\Category; use GraphQL\Language\AST\FieldNode; +use GraphQL\Language\AST\InlineFragmentNode; +use GraphQL\Language\AST\NodeKind; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; /** * Used for determining the depth information for a requested category tree in a GraphQL request @@ -17,22 +20,54 @@ class DepthCalculator /** * Calculate the total depth of a category tree inside a GraphQL request * + * @param ResolveInfo $resolveInfo * @param FieldNode $fieldNode * @return int */ - public function calculate(FieldNode $fieldNode) : int + public function calculate(ResolveInfo $resolveInfo, FieldNode $fieldNode) : int { $selections = $fieldNode->selectionSet->selections ?? []; $depth = count($selections) ? 1 : 0; $childrenDepth = [0]; foreach ($selections as $node) { - if ($node->kind === 'InlineFragment' || null !== $node->alias) { + if (isset($node->alias) && null !== $node->alias) { continue; } - $childrenDepth[] = $this->calculate($node); + if ($node->kind === NodeKind::INLINE_FRAGMENT) { + $childrenDepth[] = $this->addInlineFragmentDepth($resolveInfo, $node); + } elseif ($node->kind === NodeKind::FRAGMENT_SPREAD && isset($resolveInfo->fragments[$node->name->value])) { + foreach ($resolveInfo->fragments[$node->name->value]->selectionSet->selections as $spreadNode) { + $childrenDepth[] = $this->calculate($resolveInfo, $spreadNode); + } + } else { + $childrenDepth[] = $this->calculate($resolveInfo, $node); + } } return $depth + max($childrenDepth); } + + /** + * Add inline fragment fields into calculating of category depth + * + * @param ResolveInfo $resolveInfo + * @param InlineFragmentNode $inlineFragmentField + * @param array $depth + * @return int[] + */ + private function addInlineFragmentDepth(ResolveInfo $resolveInfo, InlineFragmentNode $inlineFragmentField, $depth = []) + { + $selections = $inlineFragmentField->selectionSet->selections; + /** @var FieldNode $field */ + foreach ($selections as $field) { + if ($field->kind === NodeKind::INLINE_FRAGMENT) { + $depth[] = $this->addInlineFragmentDepth($resolveInfo, $field, $depth); + } elseif ($field->selectionSet && $field->selectionSet->selections) { + $depth[] = $this->calculate($resolveInfo, $field); + } + } + + return $depth ? max($depth) : 0; + } } diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Categories.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Categories.php index 535fe3a80cd25..bc0eb2145fe34 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Categories.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Categories.php @@ -7,18 +7,18 @@ namespace Magento\CatalogGraphQl\Model\Resolver; -use Magento\CatalogGraphQl\Model\Resolver\Product\ProductCategories; -use Magento\Framework\Exception\LocalizedException; -use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Catalog\Api\Data\CategoryInterface; use Magento\Catalog\Model\ResourceModel\Category\Collection; use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory; use Magento\CatalogGraphQl\Model\AttributesJoiner; +use Magento\CatalogGraphQl\Model\Category\Hydrator as CategoryHydrator; +use Magento\CatalogGraphQl\Model\Resolver\Product\ProductCategories; use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\CustomAttributesFlattener; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\GraphQl\Config\Element\Field; -use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Query\Resolver\ValueFactory; -use Magento\CatalogGraphQl\Model\Category\Hydrator as CategoryHydrator; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Store\Model\StoreManagerInterface; /** @@ -112,6 +112,7 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value $categoryIds = $this->productCategories->getCategoryIdsByProduct((int)$product->getId(), (int)$storeId); $this->categoryIds = array_merge($this->categoryIds, $categoryIds); $that = $this; + $that->attributesJoiner->setResolverInfo($info); return $this->valueFactory->create( function () use ($that, $categoryIds, $info) { diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductFieldsSelector.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductFieldsSelector.php index 9ddad4e6451fa..3139c35774008 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductFieldsSelector.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductFieldsSelector.php @@ -7,6 +7,7 @@ namespace Magento\CatalogGraphQl\Model\Resolver\Product; +use GraphQL\Language\AST\NodeKind; use Magento\Framework\GraphQl\Query\FieldTranslator; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; @@ -43,9 +44,9 @@ public function getProductFieldsFromInfo(ResolveInfo $info, string $productNodeN continue; } foreach ($node->selectionSet->selections as $selectionNode) { - if ($selectionNode->kind === 'InlineFragment') { + if ($selectionNode->kind === NodeKind::INLINE_FRAGMENT) { foreach ($selectionNode->selectionSet->selections as $inlineSelection) { - if ($inlineSelection->kind === 'InlineFragment') { + if ($inlineSelection->kind === NodeKind::INLINE_FRAGMENT) { continue; } $fieldNames[] = $this->fieldTranslator->translate($inlineSelection->name->value); diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/CategoryTree.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/CategoryTree.php index fc5a563c82b4e..649ecab9c5a0f 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/CategoryTree.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/CategoryTree.php @@ -8,15 +8,16 @@ namespace Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider; use GraphQL\Language\AST\FieldNode; -use Magento\CatalogGraphQl\Model\Category\DepthCalculator; -use Magento\CatalogGraphQl\Model\Category\LevelCalculator; -use Magento\Framework\EntityManager\MetadataPool; -use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use GraphQL\Language\AST\NodeKind; use Magento\Catalog\Api\Data\CategoryInterface; +use Magento\Catalog\Model\Category; use Magento\Catalog\Model\ResourceModel\Category\Collection; use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory; use Magento\CatalogGraphQl\Model\AttributesJoiner; -use Magento\Catalog\Model\Category; +use Magento\CatalogGraphQl\Model\Category\DepthCalculator; +use Magento\CatalogGraphQl\Model\Category\LevelCalculator; +use Magento\Framework\EntityManager\MetadataPool; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; /** * Category tree data provider @@ -53,6 +54,11 @@ class CategoryTree */ private $metadata; + /** + * @var ResolveInfo + */ + private $resolverInfo; + /** * @param CollectionFactory $collectionFactory * @param AttributesJoiner $attributesJoiner @@ -85,8 +91,10 @@ public function getTree(ResolveInfo $resolveInfo, int $rootCategoryId): \Iterato { $categoryQuery = $resolveInfo->fieldNodes[0]; $collection = $this->collectionFactory->create(); + $this->resolverInfo = $resolveInfo; + $this->attributesJoiner->setResolverInfo($resolveInfo); $this->joinAttributesRecursively($collection, $categoryQuery); - $depth = $this->depthCalculator->calculate($categoryQuery); + $depth = $this->depthCalculator->calculate($resolveInfo, $categoryQuery); $level = $this->levelCalculator->calculate($rootCategoryId); // If root category is being filter, we've to remove first slash @@ -137,11 +145,15 @@ private function joinAttributesRecursively(Collection $collection, FieldNode $fi /** @var FieldNode $node */ foreach ($subSelection as $node) { - if ($node->kind === 'InlineFragment') { + if ($node->kind === NodeKind::INLINE_FRAGMENT) { continue; + } elseif ($node->kind === NodeKind::FRAGMENT_SPREAD && isset($this->resolverInfo->fragments[$node->name->value])) { + foreach ($this->resolverInfo->fragments[$node->name->value]->selectionSet->selections as $spreadNode) { + $this->joinAttributesRecursively($collection, $spreadNode); + } + } else { + $this->joinAttributesRecursively($collection, $node); } - - $this->joinAttributesRecursively($collection, $node); } } } diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php index 4c83afb89cc46..298cfd2b0e99c 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php @@ -110,7 +110,7 @@ public function getList( $searchResults = $this->searchResultsFactory->create(); $searchResults->setSearchCriteria($searchCriteriaForCollection); $searchResults->setItems($collection->getItems()); - $searchResults->setTotalCount($searchResult->getTotalCount()); + $searchResults->setTotalCount($collection->getSize()); return $searchResults; } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryListTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryListTest.php index 00eb235cb4dc3..d6477c82513e9 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryListTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryListTest.php @@ -714,4 +714,60 @@ private function assertCategoryChildren(array $category, array $expectedChildren $this->assertResponseFields($category['children'][$i], $expectedChild); } } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + */ + public function testFilterCategoryInlineFragment() + { + $query = <<graphQlQuery($query); + $this->assertArrayNotHasKey('errors', $result); + $this->assertCount(1, $result['categoryList']); + $this->assertEquals($result['categoryList'][0]['name'], 'Category 2'); + $this->assertEquals($result['categoryList'][0]['url_path'], 'category-2'); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + */ + public function testFilterCategoryNamedFragment() + { + $query = <<graphQlQuery($query); + $this->assertArrayNotHasKey('errors', $result); + $this->assertCount(1, $result['categoryList']); + $this->assertEquals($result['categoryList'][0]['name'], 'Category 2'); + $this->assertEquals($result['categoryList'][0]['url_path'], 'category-2'); + } } From f6e2e294e7bb03de0502e78133ef7022bb126a89 Mon Sep 17 00:00:00 2001 From: Munkh-Ulzii Balidar Date: Tue, 16 Jun 2020 11:20:19 +0200 Subject: [PATCH 07/28] 28584 fix dependency --- .../CatalogGraphQl/Model/AttributesJoiner.php | 46 ++++++++----------- .../Model/Resolver/Categories.php | 5 +- .../Products/DataProvider/CategoryTree.php | 28 ++++------- 3 files changed, 32 insertions(+), 47 deletions(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/AttributesJoiner.php b/app/code/Magento/CatalogGraphQl/Model/AttributesJoiner.php index ee00cdeb52c30..b7bdb6ddbb9d7 100644 --- a/app/code/Magento/CatalogGraphQl/Model/AttributesJoiner.php +++ b/app/code/Magento/CatalogGraphQl/Model/AttributesJoiner.php @@ -8,7 +8,6 @@ namespace Magento\CatalogGraphQl\Model; use GraphQL\Language\AST\FieldNode; -use GraphQL\Language\AST\FragmentSpreadNode; use GraphQL\Language\AST\InlineFragmentNode; use GraphQL\Language\AST\NodeKind; use Magento\Eav\Model\Entity\Collection\AbstractCollection; @@ -34,11 +33,6 @@ class AttributesJoiner */ private $fieldToAttributeMap = []; - /** - * @var ResolveInfo - */ - private $resolverInfo = null; - /** * @param array $fieldToAttributeMap */ @@ -52,11 +46,12 @@ public function __construct(array $fieldToAttributeMap = []) * * @param FieldNode $fieldNode * @param AbstractCollection $collection + * @param ResolveInfo $resolverInfo * @return void */ - public function join(FieldNode $fieldNode, AbstractCollection $collection): void + public function join(FieldNode $fieldNode, AbstractCollection $collection, ResolveInfo $resolverInfo): void { - foreach ($this->getQueryFields($fieldNode) as $field) { + foreach ($this->getQueryFields($fieldNode, $resolverInfo) as $field) { $this->addFieldToCollection($collection, $field); } } @@ -65,9 +60,10 @@ public function join(FieldNode $fieldNode, AbstractCollection $collection): void * Get an array of queried fields. * * @param FieldNode $fieldNode + * @param ResolveInfo $resolverInfo * @return string[] */ - public function getQueryFields(FieldNode $fieldNode): array + public function getQueryFields(FieldNode $fieldNode, ResolveInfo $resolveInfo): array { if (null === $this->getFieldNodeSelections($fieldNode)) { $query = $fieldNode->selectionSet->selections; @@ -75,12 +71,14 @@ public function getQueryFields(FieldNode $fieldNode): array /** @var FieldNode $field */ foreach ($query as $field) { if ($field->kind === NodeKind::INLINE_FRAGMENT) { - $inlineFragmentFields = $this->addInlineFragmentFields($field); + $inlineFragmentFields = $this->addInlineFragmentFields($resolveInfo, $field); $selectedFields = array_merge($selectedFields, $inlineFragmentFields); - } elseif ($field->kind === NodeKind::FRAGMENT_SPREAD && isset($this->resolverInfo->fragments[$field->name->value])) { - foreach ($this->resolverInfo->fragments[$field->name->value]->selectionSet->selections as $spreadNode) { + } elseif ($field->kind === NodeKind::FRAGMENT_SPREAD && + ($spreadFragmentNode = $resolveInfo->fragments[$field->name->value])) { + + foreach ($spreadFragmentNode->selectionSet->selections as $spreadNode) { if (isset($spreadNode->selectionSet->selections)) { - $fragmentSpreadFields = $this->getQueryFields($spreadNode); + $fragmentSpreadFields = $this->getQueryFields($spreadNode, $resolveInfo); $selectedFields = array_merge($selectedFields, $fragmentSpreadFields); } else { $selectedFields[] = $spreadNode->name->value; @@ -90,7 +88,7 @@ public function getQueryFields(FieldNode $fieldNode): array $selectedFields[] = $field->name->value; } } - $this->setSelectionsForFieldNode($fieldNode, $selectedFields); + $this->setSelectionsForFieldNode($fieldNode, array_unique($selectedFields)); } return $this->getFieldNodeSelections($fieldNode); @@ -99,19 +97,23 @@ public function getQueryFields(FieldNode $fieldNode): array /** * Add fields from inline fragment nodes * + * @param ResolveInfo $resolveInfo * @param InlineFragmentNode $inlineFragmentField * @param array $inlineFragmentFields * @return string[] */ - private function addInlineFragmentFields(InlineFragmentNode $inlineFragmentField, $inlineFragmentFields = []) - { + private function addInlineFragmentFields( + ResolveInfo $resolveInfo, + InlineFragmentNode $inlineFragmentField, + $inlineFragmentFields = [] + ): array { $query = $inlineFragmentField->selectionSet->selections; /** @var FieldNode $field */ foreach ($query as $field) { if ($field->kind === NodeKind::INLINE_FRAGMENT) { - $this->addInlineFragmentFields($field, $inlineFragmentFields); + $this->addInlineFragmentFields($resolveInfo, $field, $inlineFragmentFields); } elseif (isset($field->selectionSet->selections)) { - if (is_array($queryFields = $this->getQueryFields($field))) { + if (is_array($queryFields = $this->getQueryFields($field, $resolveInfo))) { $inlineFragmentFields = array_merge($inlineFragmentFields, $queryFields); } } else { @@ -170,12 +172,4 @@ private function setSelectionsForFieldNode(FieldNode $fieldNode, array $selected { $this->queryFields[$fieldNode->name->value][$fieldNode->name->loc->start] = $selectedFields; } - - /** - * @param ResolveInfo $resolverInfo - */ - public function setResolverInfo(ResolveInfo $resolverInfo): void - { - $this->resolverInfo = $resolverInfo; - } } diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Categories.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Categories.php index bc0eb2145fe34..d7118d71db89b 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Categories.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Categories.php @@ -112,7 +112,6 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value $categoryIds = $this->productCategories->getCategoryIdsByProduct((int)$product->getId(), (int)$storeId); $this->categoryIds = array_merge($this->categoryIds, $categoryIds); $that = $this; - $that->attributesJoiner->setResolverInfo($info); return $this->valueFactory->create( function () use ($that, $categoryIds, $info) { @@ -122,7 +121,7 @@ function () use ($that, $categoryIds, $info) { } if (!$this->collection->isLoaded()) { - $that->attributesJoiner->join($info->fieldNodes[0], $this->collection); + $that->attributesJoiner->join($info->fieldNodes[0], $this->collection, $info); $this->collection->addIdFilter($this->categoryIds); } /** @var CategoryInterface | \Magento\Catalog\Model\Category $item */ @@ -131,7 +130,7 @@ function () use ($that, $categoryIds, $info) { // Try to extract all requested fields from the loaded collection data $categories[$item->getId()] = $this->categoryHydrator->hydrateCategory($item, true); $categories[$item->getId()]['model'] = $item; - $requestedFields = $that->attributesJoiner->getQueryFields($info->fieldNodes[0]); + $requestedFields = $that->attributesJoiner->getQueryFields($info->fieldNodes[0], $info); $extractedFields = array_keys($categories[$item->getId()]); $foundFields = array_intersect($requestedFields, $extractedFields); if (count($requestedFields) === count($foundFields)) { diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/CategoryTree.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/CategoryTree.php index 649ecab9c5a0f..c553d4486f9e9 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/CategoryTree.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/CategoryTree.php @@ -54,11 +54,6 @@ class CategoryTree */ private $metadata; - /** - * @var ResolveInfo - */ - private $resolverInfo; - /** * @param CollectionFactory $collectionFactory * @param AttributesJoiner $attributesJoiner @@ -91,9 +86,7 @@ public function getTree(ResolveInfo $resolveInfo, int $rootCategoryId): \Iterato { $categoryQuery = $resolveInfo->fieldNodes[0]; $collection = $this->collectionFactory->create(); - $this->resolverInfo = $resolveInfo; - $this->attributesJoiner->setResolverInfo($resolveInfo); - $this->joinAttributesRecursively($collection, $categoryQuery); + $this->joinAttributesRecursively($collection, $categoryQuery, $resolveInfo); $depth = $this->depthCalculator->calculate($resolveInfo, $categoryQuery); $level = $this->levelCalculator->calculate($rootCategoryId); @@ -132,28 +125,27 @@ public function getTree(ResolveInfo $resolveInfo, int $rootCategoryId): \Iterato * * @param Collection $collection * @param FieldNode $fieldNode + * @param ResolveInfo $resolveInfo * @return void */ - private function joinAttributesRecursively(Collection $collection, FieldNode $fieldNode) : void - { + private function joinAttributesRecursively( + Collection $collection, + FieldNode $fieldNode, + ResolveInfo $resolveInfo + ): void { if (!isset($fieldNode->selectionSet->selections)) { return; } $subSelection = $fieldNode->selectionSet->selections; - $this->attributesJoiner->join($fieldNode, $collection); + $this->attributesJoiner->join($fieldNode, $collection, $resolveInfo); /** @var FieldNode $node */ foreach ($subSelection as $node) { - if ($node->kind === NodeKind::INLINE_FRAGMENT) { + if ($node->kind === NodeKind::INLINE_FRAGMENT || $node->kind === NodeKind::FRAGMENT_SPREAD) { continue; - } elseif ($node->kind === NodeKind::FRAGMENT_SPREAD && isset($this->resolverInfo->fragments[$node->name->value])) { - foreach ($this->resolverInfo->fragments[$node->name->value]->selectionSet->selections as $spreadNode) { - $this->joinAttributesRecursively($collection, $spreadNode); - } - } else { - $this->joinAttributesRecursively($collection, $node); } + $this->joinAttributesRecursively($collection, $node, $resolveInfo); } } } From e125f004b8637efa8f26e578ae71afde1cf5d51e Mon Sep 17 00:00:00 2001 From: Marjan Petkovski Date: Tue, 16 Jun 2020 13:02:18 +0200 Subject: [PATCH 08/28] magento/magento2#28580: False positive behavior of testQueryCustomAttributeField Set attributeSetId on custom attribute creation. --- .../testsuite/Magento/GraphQl/Catalog/ProductViewTest.php | 4 ++-- .../_files/product_simple_with_custom_attribute.php | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php index 99fdfb2cf1b00..5b2a318c23ac2 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php @@ -701,10 +701,10 @@ private function assertMediaGalleryEntries($product, $actualResponse) */ private function assertCustomAttribute($actualResponse) { - $customAttribute = null; + $customAttribute = 'customAttributeValue'; $this->assertEquals($customAttribute, $actualResponse['attribute_code_custom']); } - + /** * @param ProductInterface $product * @param $actualResponse diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_custom_attribute.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_custom_attribute.php index 4ed783100fa98..7c8ce4c63034d 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_custom_attribute.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_custom_attribute.php @@ -20,6 +20,9 @@ $entityTypeId = $entityModel->setType(\Magento\Catalog\Model\Product::ENTITY)->getTypeId(); $groupId = $installer->getDefaultAttributeGroupId($entityTypeId, $attributeSetId); +/** @var \Magento\Catalog\Model\Product $product */ +$product = $productRepository->get('simple', true); + /** @var $attribute \Magento\Catalog\Model\ResourceModel\Eav\Attribute */ $attribute = $objectManager->create(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class); $attribute->setAttributeCode( @@ -30,6 +33,8 @@ 'text' )->setFrontendLabel( 'custom_attributes_frontend_label' +)->setAttributeSetId( + $product->getDefaultAttributeSetId() )->setAttributeGroupId( $groupId )->setIsFilterable( @@ -40,8 +45,6 @@ $attribute->getBackendTypeByInput($attribute->getFrontendInput()) )->save(); -$product = $productRepository->get('simple', true); - $product->setCustomAttribute($attribute->getAttributeCode(), 'customAttributeValue'); $productRepository->save($product); From 256806aabbb802a545393c07bc8b8135dc7126e9 Mon Sep 17 00:00:00 2001 From: Dmitriy Gallyamov Date: Tue, 16 Jun 2020 16:48:31 +0300 Subject: [PATCH 09/28] magento/magento2#28628: GraphQL price range numeric values - Fixed price range wildcard values and associated test cases --- .../Model/Layer/Filter/Price/Render.php | 6 ++--- .../Model/Layer/Filter/Price.php | 25 +++++++++---------- .../Aggregation/Builder/Dynamic.php | 6 +---- .../Category/Bundle/PriceFilterTest.php | 2 +- .../Navigation/Category/PriceFilterTest.php | 24 +++++++++--------- .../Search/Dynamic/Algorithm/Improved.php | 5 ++-- 6 files changed, 30 insertions(+), 38 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Layer/Filter/Price/Render.php b/app/code/Magento/Catalog/Model/Layer/Filter/Price/Render.php index 77dedb9eb0121..59c0e11f7918b 100644 --- a/app/code/Magento/Catalog/Model/Layer/Filter/Price/Render.php +++ b/app/code/Magento/Catalog/Model/Layer/Filter/Price/Render.php @@ -81,12 +81,10 @@ public function renderRangeData($range, $dbRanges) if (empty($dbRanges)) { return []; } - $lastIndex = array_keys($dbRanges); - $lastIndex = $lastIndex[count($lastIndex) - 1]; foreach ($dbRanges as $index => $count) { - $fromPrice = $index == 1 ? '' : ($index - 1) * $range; - $toPrice = $index == $lastIndex ? '' : $index * $range; + $fromPrice = $index == 1 ? 0 : ($index - 1) * $range; + $toPrice = $index * $range; $this->itemDataBuilder->addItemData( $this->renderRangeLabel($fromPrice, $toPrice), $fromPrice . '-' . $toPrice, diff --git a/app/code/Magento/CatalogSearch/Model/Layer/Filter/Price.php b/app/code/Magento/CatalogSearch/Model/Layer/Filter/Price.php index 332bb991bf29f..66ea2ac09505c 100644 --- a/app/code/Magento/CatalogSearch/Model/Layer/Filter/Price.php +++ b/app/code/Magento/CatalogSearch/Model/Layer/Filter/Price.php @@ -138,7 +138,7 @@ public function apply(\Magento\Framework\App\RequestInterface $request) $this->dataProvider->setPriorIntervals($priorFilters); } - list($from, $to) = $filter; + [$from, $to] = $filter; $this->getLayer()->getProductCollection()->addFieldToFilter( 'price', @@ -176,15 +176,16 @@ public function getCurrencyRate() * * @param float|string $fromPrice * @param float|string $toPrice + * @param boolean $isLast * @return float|\Magento\Framework\Phrase */ - protected function _renderRangeLabel($fromPrice, $toPrice) + protected function _renderRangeLabel($fromPrice, $toPrice, $isLast = false) { $fromPrice = empty($fromPrice) ? 0 : $fromPrice * $this->getCurrencyRate(); $toPrice = empty($toPrice) ? $toPrice : $toPrice * $this->getCurrencyRate(); $formattedFromPrice = $this->priceCurrency->format($fromPrice); - if ($toPrice === '') { + if ($isLast) { return __('%1 and above', $formattedFromPrice); } elseif ($fromPrice == $toPrice && $this->dataProvider->getOnePriceIntervalValue()) { return $formattedFromPrice; @@ -215,12 +216,15 @@ protected function _getItemsData() $data = []; if (count($facets) > 1) { // two range minimum + $lastFacet = array_key_last($facets); foreach ($facets as $key => $aggregation) { $count = $aggregation['count']; if (strpos($key, '_') === false) { continue; } - $data[] = $this->prepareData($key, $count, $data); + + $isLast = $lastFacet === $key; + $data[] = $this->prepareData($key, $count, $isLast); } } @@ -264,18 +268,13 @@ protected function getFrom($from) * * @param string $key * @param int $count + * @param boolean $isLast * @return array */ - private function prepareData($key, $count) + private function prepareData($key, $count, $isLast = false) { - list($from, $to) = explode('_', $key); - if ($from == '*') { - $from = $this->getFrom($to); - } - if ($to == '*') { - $to = $this->getTo($to); - } - $label = $this->_renderRangeLabel($from, $to); + [$from, $to] = explode('_', $key); + $label = $this->_renderRangeLabel($from, $to, $isLast); $value = $from . '-' . $to . $this->dataProvider->getAdditionalRequestData(); $data = [ diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/Aggregation/Builder/Dynamic.php b/app/code/Magento/Elasticsearch/SearchAdapter/Aggregation/Builder/Dynamic.php index 1e106023ea00d..ec9f007f70936 100644 --- a/app/code/Magento/Elasticsearch/SearchAdapter/Aggregation/Builder/Dynamic.php +++ b/app/code/Magento/Elasticsearch/SearchAdapter/Aggregation/Builder/Dynamic.php @@ -77,11 +77,7 @@ private function prepareData($data) { $resultData = []; foreach ($data as $value) { - $from = is_numeric($value['from']) ? $value['from'] : '*'; - $to = is_numeric($value['to']) ? $value['to'] : '*'; - unset($value['from'], $value['to']); - - $rangeName = "{$from}_{$to}"; + $rangeName = "{$value['from']}_{$value['to']}"; $resultData[$rangeName] = array_merge(['value' => $rangeName], $value); } diff --git a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/Bundle/PriceFilterTest.php b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/Bundle/PriceFilterTest.php index dd4fdde250c03..b6508e3b3dfda 100644 --- a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/Bundle/PriceFilterTest.php +++ b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/Bundle/PriceFilterTest.php @@ -53,7 +53,7 @@ public function testGetFilters(): void ['is_filterable' => '1'], [ ['label' => '$10.00 - $19.99', 'value' => '10-20', 'count' => 1], - ['label' => '$20.00 and above', 'value' => '20-', 'count' => 1], + ['label' => '$20.00 and above', 'value' => '20-30', 'count' => 1], ], 'Category 1' ); diff --git a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/PriceFilterTest.php b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/PriceFilterTest.php index 3b2673b18635a..97928463620f4 100644 --- a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/PriceFilterTest.php +++ b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/PriceFilterTest.php @@ -71,15 +71,15 @@ public function getFiltersDataProvider(): array 'expectation' => [ ['label' => '$10.00 - $19.99', 'value' => '10-20', 'count' => 1], ['label' => '$20.00 - $29.99', 'value' => '20-30', 'count' => 1], - ['label' => '$50.00 and above', 'value' => '50-', 'count' => 1], + ['label' => '$50.00 and above', 'value' => '50-60', 'count' => 1], ], ], 'auto_calculation_variation_with_big_price_difference' => [ 'config' => ['catalog/layered_navigation/price_range_calculation' => 'auto'], 'products_data' => ['simple1000' => 10.00, 'simple1001' => 20.00, 'simple1002' => 300.00], 'expectation' => [ - ['label' => '$0.00 - $99.99', 'value' => '-100', 'count' => 2], - ['label' => '$300.00 and above', 'value' => '300-', 'count' => 1], + ['label' => '$0.00 - $99.99', 'value' => '0-100', 'count' => 2], + ['label' => '$300.00 and above', 'value' => '300-400', 'count' => 1], ], ], 'auto_calculation_variation_with_fixed_price_step' => [ @@ -88,7 +88,7 @@ public function getFiltersDataProvider(): array 'expectation' => [ ['label' => '$300.00 - $399.99', 'value' => '300-400', 'count' => 1], ['label' => '$400.00 - $499.99', 'value' => '400-500', 'count' => 1], - ['label' => '$500.00 and above', 'value' => '500-', 'count' => 1], + ['label' => '$500.00 and above', 'value' => '500-600', 'count' => 1], ], ], 'improved_calculation_variation_with_small_price_difference' => [ @@ -98,8 +98,8 @@ public function getFiltersDataProvider(): array ], 'products_data' => ['simple1000' => 10.00, 'simple1001' => 20.00, 'simple1002' => 50.00], 'expectation' => [ - ['label' => '$0.00 - $49.99', 'value' => '-50', 'count' => 2], - ['label' => '$50.00 and above', 'value' => '50-', 'count' => 1], + ['label' => '$0.00 - $19.99', 'value' => '0-20', 'count' => 1], + ['label' => '$20.00 and above', 'value' => '20-50', 'count' => 2], ], ], 'improved_calculation_variation_with_big_price_difference' => [ @@ -109,8 +109,8 @@ public function getFiltersDataProvider(): array ], 'products_data' => ['simple1000' => 10.00, 'simple1001' => 20.00, 'simple1002' => 300.00], 'expectation' => [ - ['label' => '$0.00 - $299.99', 'value' => '-300', 'count' => 2.0], - ['label' => '$300.00 and above', 'value' => '300-', 'count' => 1.0], + ['label' => '$0.00 - $19.99', 'value' => '0-20', 'count' => 1], + ['label' => '$20.00 and above', 'value' => '20-300', 'count' => 2], ], ], 'manual_calculation_with_price_step_200' => [ @@ -121,7 +121,7 @@ public function getFiltersDataProvider(): array 'products_data' => ['simple1000' => 300.00, 'simple1001' => 300.00, 'simple1002' => 500.00], 'expectation' => [ ['label' => '$200.00 - $399.99', 'value' => '200-400', 'count' => 2], - ['label' => '$400.00 and above', 'value' => '400-', 'count' => 1], + ['label' => '$400.00 and above', 'value' => '400-600', 'count' => 1], ], ], 'manual_calculation_with_price_step_10' => [ @@ -132,7 +132,7 @@ public function getFiltersDataProvider(): array 'products_data' => ['simple1000' => 300.00, 'simple1001' => 300.00, 'simple1002' => 500.00], 'expectation' => [ ['label' => '$300.00 - $309.99', 'value' => '300-310', 'count' => 2], - ['label' => '$500.00 and above', 'value' => '500-', 'count' => 1], + ['label' => '$500.00 and above', 'value' => '500-510', 'count' => 1], ], ], 'manual_calculation_with_number_of_intervals_10' => [ @@ -145,7 +145,7 @@ public function getFiltersDataProvider(): array 'expectation' => [ ['label' => '$10.00 - $19.99', 'value' => '10-20', 'count' => 1], ['label' => '$20.00 - $29.99', 'value' => '20-30', 'count' => 1], - ['label' => '$30.00 and above', 'value' => '30-', 'count' => 1], + ['label' => '$30.00 and above', 'value' => '30-40', 'count' => 1], ], ], 'manual_calculation_with_number_of_intervals_2' => [ @@ -157,7 +157,7 @@ public function getFiltersDataProvider(): array 'products_data' => ['simple1000' => 10.00, 'simple1001' => 20.00, 'simple1002' => 30.00], 'expectation' => [ ['label' => '$10.00 - $19.99', 'value' => '10-20', 'count' => 1], - ['label' => '$20.00 and above', 'value' => '20-', 'count' => 2], + ['label' => '$20.00 and above', 'value' => '20-30', 'count' => 2], ], ], ]; diff --git a/lib/internal/Magento/Framework/Search/Dynamic/Algorithm/Improved.php b/lib/internal/Magento/Framework/Search/Dynamic/Algorithm/Improved.php index a3e9ed61824ed..1639ee3d75428 100644 --- a/lib/internal/Magento/Framework/Search/Dynamic/Algorithm/Improved.php +++ b/lib/internal/Magento/Framework/Search/Dynamic/Algorithm/Improved.php @@ -64,13 +64,12 @@ public function getItems( $aggregations['count'] ); - $this->algorithm->setLimits($aggregations['min'], $aggregations['max'] + 0.01); + $this->algorithm->setLimits($aggregations['min'], $aggregations['max']); $interval = $this->dataProvider->getInterval($bucket, $dimensions, $entityStorage); $data = $this->algorithm->calculateSeparators($interval); - $data[0]['from'] = ''; // We should not calculate min and max value - $data[count($data) - 1]['to'] = ''; + $data[0]['from'] = 0; $dataSize = count($data); for ($key = 0; $key < $dataSize; $key++) { From e4c613223a00c82c2838ac2f4fdb47ff9675b2a4 Mon Sep 17 00:00:00 2001 From: Dmitriy Gallyamov Date: Tue, 16 Jun 2020 17:48:55 +0300 Subject: [PATCH 10/28] magento/magento2#28628: GraphQL price range numeric values - Code style fixes --- app/code/Magento/Catalog/Model/Layer/Filter/Price/Render.php | 2 ++ .../Elasticsearch/SearchAdapter/Aggregation/Builder/Dynamic.php | 2 +- .../Magento/Framework/Search/Dynamic/Algorithm/Improved.php | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Layer/Filter/Price/Render.php b/app/code/Magento/Catalog/Model/Layer/Filter/Price/Render.php index 59c0e11f7918b..3494fd00a8b6c 100644 --- a/app/code/Magento/Catalog/Model/Layer/Filter/Price/Render.php +++ b/app/code/Magento/Catalog/Model/Layer/Filter/Price/Render.php @@ -72,6 +72,8 @@ public function renderRangeLabel($fromPrice, $toPrice) } /** + * Prepare range data + * * @param int $range * @param int[] $dbRanges * @return array diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/Aggregation/Builder/Dynamic.php b/app/code/Magento/Elasticsearch/SearchAdapter/Aggregation/Builder/Dynamic.php index ec9f007f70936..f4b55ce43c421 100644 --- a/app/code/Magento/Elasticsearch/SearchAdapter/Aggregation/Builder/Dynamic.php +++ b/app/code/Magento/Elasticsearch/SearchAdapter/Aggregation/Builder/Dynamic.php @@ -35,7 +35,7 @@ public function __construct(Repository $algorithmRepository, EntityStorageFactor } /** - * {@inheritdoc} + * @inheritdoc */ public function build( RequestBucketInterface $bucket, diff --git a/lib/internal/Magento/Framework/Search/Dynamic/Algorithm/Improved.php b/lib/internal/Magento/Framework/Search/Dynamic/Algorithm/Improved.php index 1639ee3d75428..c4f6c67200b2b 100644 --- a/lib/internal/Magento/Framework/Search/Dynamic/Algorithm/Improved.php +++ b/lib/internal/Magento/Framework/Search/Dynamic/Algorithm/Improved.php @@ -44,7 +44,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function getItems( BucketInterface $bucket, From 886718514a4569351414187fc071646c109fbd46 Mon Sep 17 00:00:00 2001 From: Dmitriy Gallyamov Date: Tue, 16 Jun 2020 17:53:09 +0300 Subject: [PATCH 11/28] magento/magento2#28628: GraphQL price range numeric values - Fixed tests --- .../Test/Unit/SearchAdapter/Dynamic/DataProviderTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/Dynamic/DataProviderTest.php b/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/Dynamic/DataProviderTest.php index c5b9089acd91c..0595b667f4ee8 100644 --- a/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/Dynamic/DataProviderTest.php +++ b/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/Dynamic/DataProviderTest.php @@ -390,13 +390,13 @@ public function testPrepareData() { $expectedResult = [ [ - 'from' => '', + 'from' => 0, 'to' => 10, 'count' => 1, ], [ 'from' => 10, - 'to' => '', + 'to' => 20, 'count' => 1, ], ]; From 6588700ba9bbf60236b9987048d3873bc68b10fe Mon Sep 17 00:00:00 2001 From: Munkh-Ulzii Balidar Date: Tue, 16 Jun 2020 23:27:30 +0200 Subject: [PATCH 12/28] 28584 fix mismatched variable name, modify array_merge in loop --- .../CatalogGraphQl/Model/AttributesJoiner.php | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/AttributesJoiner.php b/app/code/Magento/CatalogGraphQl/Model/AttributesJoiner.php index b7bdb6ddbb9d7..0bfd9d58ec969 100644 --- a/app/code/Magento/CatalogGraphQl/Model/AttributesJoiner.php +++ b/app/code/Magento/CatalogGraphQl/Model/AttributesJoiner.php @@ -46,12 +46,12 @@ public function __construct(array $fieldToAttributeMap = []) * * @param FieldNode $fieldNode * @param AbstractCollection $collection - * @param ResolveInfo $resolverInfo + * @param ResolveInfo $resolveInfo * @return void */ - public function join(FieldNode $fieldNode, AbstractCollection $collection, ResolveInfo $resolverInfo): void + public function join(FieldNode $fieldNode, AbstractCollection $collection, ResolveInfo $resolveInfo): void { - foreach ($this->getQueryFields($fieldNode, $resolverInfo) as $field) { + foreach ($this->getQueryFields($fieldNode, $resolveInfo) as $field) { $this->addFieldToCollection($collection, $field); } } @@ -60,7 +60,7 @@ public function join(FieldNode $fieldNode, AbstractCollection $collection, Resol * Get an array of queried fields. * * @param FieldNode $fieldNode - * @param ResolveInfo $resolverInfo + * @param ResolveInfo $resolveInfo * @return string[] */ public function getQueryFields(FieldNode $fieldNode, ResolveInfo $resolveInfo): array @@ -68,18 +68,17 @@ public function getQueryFields(FieldNode $fieldNode, ResolveInfo $resolveInfo): if (null === $this->getFieldNodeSelections($fieldNode)) { $query = $fieldNode->selectionSet->selections; $selectedFields = []; + $fragmentFields = []; /** @var FieldNode $field */ foreach ($query as $field) { if ($field->kind === NodeKind::INLINE_FRAGMENT) { - $inlineFragmentFields = $this->addInlineFragmentFields($resolveInfo, $field); - $selectedFields = array_merge($selectedFields, $inlineFragmentFields); + $fragmentFields[] = $this->addInlineFragmentFields($resolveInfo, $field); } elseif ($field->kind === NodeKind::FRAGMENT_SPREAD && ($spreadFragmentNode = $resolveInfo->fragments[$field->name->value])) { foreach ($spreadFragmentNode->selectionSet->selections as $spreadNode) { if (isset($spreadNode->selectionSet->selections)) { - $fragmentSpreadFields = $this->getQueryFields($spreadNode, $resolveInfo); - $selectedFields = array_merge($selectedFields, $fragmentSpreadFields); + $fragmentFields[] = $this->getQueryFields($spreadNode, $resolveInfo); } else { $selectedFields[] = $spreadNode->name->value; } @@ -88,6 +87,9 @@ public function getQueryFields(FieldNode $fieldNode, ResolveInfo $resolveInfo): $selectedFields[] = $field->name->value; } } + if ($fragmentFields) { + $selectedFields = array_merge($selectedFields, array_merge(...$fragmentFields)); + } $this->setSelectionsForFieldNode($fieldNode, array_unique($selectedFields)); } @@ -113,9 +115,7 @@ private function addInlineFragmentFields( if ($field->kind === NodeKind::INLINE_FRAGMENT) { $this->addInlineFragmentFields($resolveInfo, $field, $inlineFragmentFields); } elseif (isset($field->selectionSet->selections)) { - if (is_array($queryFields = $this->getQueryFields($field, $resolveInfo))) { - $inlineFragmentFields = array_merge($inlineFragmentFields, $queryFields); - } + continue; } else { $inlineFragmentFields[] = $field->name->value; } From 622e2aeda03b7dbb5d4bf6aed4ebc2b179a07387 Mon Sep 17 00:00:00 2001 From: Dmitriy Gallyamov Date: Wed, 17 Jun 2020 14:21:16 +0300 Subject: [PATCH 13/28] magento/magento2#28628: GraphQL price range numeric values - Fixed tests --- .../SearchAdapter/Aggregation/Builder/Dynamic.php | 7 +++---- .../Magento/GraphQl/Catalog/ProductSearchTest.php | 8 ++++---- .../Navigation/Category/Configurable/PriceFilterTest.php | 4 ++-- .../Block/Navigation/Search/Bundle/PriceFilterTest.php | 2 +- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/Aggregation/Builder/Dynamic.php b/app/code/Magento/Elasticsearch/SearchAdapter/Aggregation/Builder/Dynamic.php index f4b55ce43c421..548a57e55f3e2 100644 --- a/app/code/Magento/Elasticsearch/SearchAdapter/Aggregation/Builder/Dynamic.php +++ b/app/code/Magento/Elasticsearch/SearchAdapter/Aggregation/Builder/Dynamic.php @@ -46,9 +46,7 @@ public function build( /** @var DynamicBucket $bucket */ $algorithm = $this->algorithmRepository->get($bucket->getMethod(), ['dataProvider' => $dataProvider]); $data = $algorithm->getItems($bucket, $dimensions, $this->getEntityStorage($queryResult)); - $resultData = $this->prepareData($data); - - return $resultData; + return $this->prepareData($data); } /** @@ -78,7 +76,8 @@ private function prepareData($data) $resultData = []; foreach ($data as $value) { $rangeName = "{$value['from']}_{$value['to']}"; - $resultData[$rangeName] = array_merge(['value' => $rangeName], $value); + $value['value'] = $rangeName; + $resultData[$rangeName] = $value; } return $resultData; diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php index 1a95a3d6f4925..dd5b5827c8017 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php @@ -570,8 +570,8 @@ public function testSearchAndFilterByCustomAttribute() ], [ 'count' => 1, - 'label' => '40-*', - 'value' => '40_*', + 'label' => '40-50', + 'value' => '40_50', ], ], @@ -1431,8 +1431,8 @@ public function testFilterProductsForExactMatchingName() 'count' => 1, ], [ - 'label' => '20-*', - 'value' => '20_*', + 'label' => '20-30', + 'value' => '20_30', 'count' => 1, ] ] diff --git a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/Configurable/PriceFilterTest.php b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/Configurable/PriceFilterTest.php index 07882b68d62d5..e226881b9cfcc 100644 --- a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/Configurable/PriceFilterTest.php +++ b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/Configurable/PriceFilterTest.php @@ -76,7 +76,7 @@ public function getFiltersDataProvider(): array ], [ 'label' => '$60.00 and above', - 'value' => '60-', + 'value' => '60-70', 'count' => 1, ], ], @@ -94,7 +94,7 @@ public function getFiltersDataProvider(): array ], [ 'label' => '$50.00 and above', - 'value' => '50-', + 'value' => '50-60', 'count' => 1, ], ], diff --git a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Search/Bundle/PriceFilterTest.php b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Search/Bundle/PriceFilterTest.php index 435dd29e16dfa..760f4031b8844 100644 --- a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Search/Bundle/PriceFilterTest.php +++ b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Search/Bundle/PriceFilterTest.php @@ -32,7 +32,7 @@ public function testGetFilters(): void ['is_filterable_in_search' => 1], [ ['label' => '$10.00 - $19.99', 'value' => '10-20', 'count' => 1], - ['label' => '$20.00 and above', 'value' => '20-', 'count' => 1], + ['label' => '$20.00 and above', 'value' => '20-30', 'count' => 1], ] ); } From 50635d947b5d0b62496d9078fb7d46ae15f4e336 Mon Sep 17 00:00:00 2001 From: Michal Derlatka Date: Fri, 12 Jun 2020 19:21:13 +0200 Subject: [PATCH 14/28] magento/magento2#28561: GraphQL added CORS headers --- .../CorsAllowCredentialsHeaderProvider.php | 38 ++++++++++++ .../Cors/CorsAllowHeadersHeaderProvider.php | 43 ++++++++++++++ .../Cors/CorsAllowMethodsHeaderProvider.php | 44 ++++++++++++++ .../Cors/CorsAllowOriginHeaderProvider.php | 39 ++++++++++++ .../Cors/CorsMaxAgeHeaderProvider.php | 44 ++++++++++++++ .../GraphQl/Model/Cors/Configuration.php | 58 ++++++++++++++++++ .../Model/Cors/ConfigurationInterface.php | 20 +++++++ .../Magento/GraphQl/etc/adminhtml/system.xml | 59 +++++++++++++++++++ app/code/Magento/GraphQl/etc/config.xml | 16 +++++ app/code/Magento/GraphQl/etc/di.xml | 2 + app/code/Magento/GraphQl/etc/graphql/di.xml | 11 ++++ 11 files changed, 374 insertions(+) create mode 100644 app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowCredentialsHeaderProvider.php create mode 100644 app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowHeadersHeaderProvider.php create mode 100644 app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowMethodsHeaderProvider.php create mode 100644 app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowOriginHeaderProvider.php create mode 100644 app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsMaxAgeHeaderProvider.php create mode 100644 app/code/Magento/GraphQl/Model/Cors/Configuration.php create mode 100644 app/code/Magento/GraphQl/Model/Cors/ConfigurationInterface.php create mode 100644 app/code/Magento/GraphQl/etc/adminhtml/system.xml create mode 100644 app/code/Magento/GraphQl/etc/config.xml diff --git a/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowCredentialsHeaderProvider.php b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowCredentialsHeaderProvider.php new file mode 100644 index 0000000000000..3f7c912b574fc --- /dev/null +++ b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowCredentialsHeaderProvider.php @@ -0,0 +1,38 @@ +corsConfiguration = $corsConfiguration; + } + + public function getName() + { + return $this->headerName; + } + + public function getValue() + { + return true; + } + + public function canApply() : bool + { + return $this->corsConfiguration->isEnabled() && $this->corsConfiguration->isCredentialsAllowed(); + } +} diff --git a/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowHeadersHeaderProvider.php b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowHeadersHeaderProvider.php new file mode 100644 index 0000000000000..e44e7c6b1e872 --- /dev/null +++ b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowHeadersHeaderProvider.php @@ -0,0 +1,43 @@ +corsConfiguration = $corsConfiguration; + } + + public function getName() + { + return $this->headerName; + } + + public function canApply() : bool + { + return $this->corsConfiguration->isEnabled() && $this->getValue(); + } + + public function getValue() + { + return $this->corsConfiguration->getAllowedHeaders() + ? $this->corsConfiguration->getAllowedHeaders() + : $this->headerValue; + } +} diff --git a/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowMethodsHeaderProvider.php b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowMethodsHeaderProvider.php new file mode 100644 index 0000000000000..548ffc1aec3f6 --- /dev/null +++ b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowMethodsHeaderProvider.php @@ -0,0 +1,44 @@ +corsConfiguration = $corsConfiguration; + } + + public function getName() + { + return $this->headerName; + } + + public function canApply() : bool + { + return $this->corsConfiguration->isEnabled() && $this->getValue(); + } + + public function getValue() + { + return $this->corsConfiguration->getAllowedMethods() + ? $this->corsConfiguration->getAllowedMethods() + : $this->headerValue; + } +} diff --git a/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowOriginHeaderProvider.php b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowOriginHeaderProvider.php new file mode 100644 index 0000000000000..8df8c2ec6e39c --- /dev/null +++ b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowOriginHeaderProvider.php @@ -0,0 +1,39 @@ +corsConfiguration = $corsConfiguration; + } + + public function getName() + { + return $this->headerName; + } + + public function canApply() : bool + { + return $this->corsConfiguration->isEnabled() && $this->getValue(); + } + + public function getValue() + { + return $this->corsConfiguration->getAllowedOrigins(); + } +} diff --git a/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsMaxAgeHeaderProvider.php b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsMaxAgeHeaderProvider.php new file mode 100644 index 0000000000000..b74f405930caf --- /dev/null +++ b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsMaxAgeHeaderProvider.php @@ -0,0 +1,44 @@ +corsConfiguration = $corsConfiguration; + } + + public function getName() + { + return $this->headerName; + } + + public function canApply() + { + return $this->corsConfiguration->isEnabled() && $this->getValue(); + } + + public function getValue() + { + return $this->corsConfiguration->getMaxAge() + ? $this->corsConfiguration->getMaxAge() + : $this->headerValue; + } +} diff --git a/app/code/Magento/GraphQl/Model/Cors/Configuration.php b/app/code/Magento/GraphQl/Model/Cors/Configuration.php new file mode 100644 index 0000000000000..6748ea6c3c9a1 --- /dev/null +++ b/app/code/Magento/GraphQl/Model/Cors/Configuration.php @@ -0,0 +1,58 @@ +scopeConfig = $scopeConfig; + } + + public function isEnabled(): bool + { + return $this->scopeConfig->isSetFlag(self::XML_PATH_CORS_HEADERS_ENABLED); + } + + public function getAllowedOrigins(): ?string + { + return $this->scopeConfig->getValue(self::XML_PATH_CORS_ALLOWED_ORIGINS); + } + + public function getAllowedHeaders(): ?string + { + return $this->scopeConfig->getValue(self::XML_PATH_CORS_ALLOWED_HEADERS); + } + + public function getAllowedMethods(): ?string + { + return $this->scopeConfig->getValue(self::XML_PATH_CORS_ALLOWED_METHODS); + } + + public function getMaxAge(): int + { + return $this->scopeConfig->getValue(self::XML_PATH_CORS_MAX_AGE); + } + + public function isCredentialsAllowed(): bool + { + return $this->scopeConfig->isSetFlag(self::XML_PATH_CORS_ALLOW_CREDENTIALS); + } + +} diff --git a/app/code/Magento/GraphQl/Model/Cors/ConfigurationInterface.php b/app/code/Magento/GraphQl/Model/Cors/ConfigurationInterface.php new file mode 100644 index 0000000000000..bbb23abe854b6 --- /dev/null +++ b/app/code/Magento/GraphQl/Model/Cors/ConfigurationInterface.php @@ -0,0 +1,20 @@ + + + + +
+ + service + Magento_Integration::config_oauth + + + + + Magento\Config\Model\Config\Source\Yesno + + + + + + 1 + + + + + + + 1 + + + + + + + 1 + + + + + + + 1 + + + + + + Magento\Config\Model\Config\Source\Yesno + + 1 + + + +
+
+
diff --git a/app/code/Magento/GraphQl/etc/config.xml b/app/code/Magento/GraphQl/etc/config.xml new file mode 100644 index 0000000000000..76a1fac199582 --- /dev/null +++ b/app/code/Magento/GraphQl/etc/config.xml @@ -0,0 +1,16 @@ + + + + + + 0 + + + + 86400 + 0 + + + + diff --git a/app/code/Magento/GraphQl/etc/di.xml b/app/code/Magento/GraphQl/etc/di.xml index b356f33c4f4bf..79052c717bc96 100644 --- a/app/code/Magento/GraphQl/etc/di.xml +++ b/app/code/Magento/GraphQl/etc/di.xml @@ -98,4 +98,6 @@ 300 + + diff --git a/app/code/Magento/GraphQl/etc/graphql/di.xml b/app/code/Magento/GraphQl/etc/graphql/di.xml index 77fce336374dd..23d49124d1a02 100644 --- a/app/code/Magento/GraphQl/etc/graphql/di.xml +++ b/app/code/Magento/GraphQl/etc/graphql/di.xml @@ -30,4 +30,15 @@ + + + + Magento\GraphQl\Controller\HttpResponse\Cors\CorsAllowOriginHeaderProvider + Magento\GraphQl\Controller\HttpResponse\Cors\CorsAllowHeadersHeaderProvider + Magento\GraphQl\Controller\HttpResponse\Cors\CorsAllowMethodsHeaderProvider + Magento\GraphQl\Controller\HttpResponse\Cors\CorsAllowCredentialsHeaderProvider + Magento\GraphQl\Controller\HttpResponse\Cors\CorsMaxAgeHeaderProvider + + + From 433a7d0f7849eb6b18f3f583d3df22c738323b95 Mon Sep 17 00:00:00 2001 From: Marjan Petkovski Date: Thu, 18 Jun 2020 17:10:24 +0200 Subject: [PATCH 15/28] magento/magento2#25580: Address static tests issues --- .../testsuite/Magento/GraphQl/Catalog/ProductViewTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php index 5b2a318c23ac2..c6719f1862ddc 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php @@ -665,7 +665,8 @@ private function assertMediaGalleryEntries($product, $actualResponse) { $mediaGalleryEntries = $product->getMediaGalleryEntries(); $this->assertCount(1, $mediaGalleryEntries, "Precondition failed, incorrect number of media gallery entries."); - $this->assertIsArray([$actualResponse['media_gallery_entries']], + $this->assertIsArray( + [$actualResponse['media_gallery_entries']], "Media galleries field must be of an array type." ); $this->assertCount(1, $actualResponse['media_gallery_entries'], "There must be 1 record in media gallery."); From a9d6eb3265663b35abf0d799963a5c6097fbaa23 Mon Sep 17 00:00:00 2001 From: Munkh-Ulzii Balidar Date: Thu, 18 Jun 2020 22:08:12 +0200 Subject: [PATCH 16/28] 28584 fix static code style --- .../CatalogGraphQl/Model/Category/DepthCalculator.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/Category/DepthCalculator.php b/app/code/Magento/CatalogGraphQl/Model/Category/DepthCalculator.php index 6bbd163436c2c..f3dd4aafaeb0d 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Category/DepthCalculator.php +++ b/app/code/Magento/CatalogGraphQl/Model/Category/DepthCalculator.php @@ -54,10 +54,13 @@ public function calculate(ResolveInfo $resolveInfo, FieldNode $fieldNode) : int * @param ResolveInfo $resolveInfo * @param InlineFragmentNode $inlineFragmentField * @param array $depth - * @return int[] + * @return int */ - private function addInlineFragmentDepth(ResolveInfo $resolveInfo, InlineFragmentNode $inlineFragmentField, $depth = []) - { + private function addInlineFragmentDepth( + ResolveInfo $resolveInfo, + InlineFragmentNode + $inlineFragmentField, $depth = [] + ): int { $selections = $inlineFragmentField->selectionSet->selections; /** @var FieldNode $field */ foreach ($selections as $field) { From 423939c02b833056db22f42d640ff21261c5dd58 Mon Sep 17 00:00:00 2001 From: Munkh-Ulzii Balidar Date: Fri, 19 Jun 2020 10:17:50 +0200 Subject: [PATCH 17/28] 28584 fix static code style --- .../Magento/CatalogGraphQl/Model/Category/DepthCalculator.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/Category/DepthCalculator.php b/app/code/Magento/CatalogGraphQl/Model/Category/DepthCalculator.php index f3dd4aafaeb0d..ab100c7272ba0 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Category/DepthCalculator.php +++ b/app/code/Magento/CatalogGraphQl/Model/Category/DepthCalculator.php @@ -58,8 +58,8 @@ public function calculate(ResolveInfo $resolveInfo, FieldNode $fieldNode) : int */ private function addInlineFragmentDepth( ResolveInfo $resolveInfo, - InlineFragmentNode - $inlineFragmentField, $depth = [] + InlineFragmentNode $inlineFragmentField, + $depth = [] ): int { $selections = $inlineFragmentField->selectionSet->selections; /** @var FieldNode $field */ From b79c484c3b0f9153a78edb3aa96f1332c6157fd8 Mon Sep 17 00:00:00 2001 From: Michal Derlatka Date: Mon, 22 Jun 2020 14:47:12 +0200 Subject: [PATCH 18/28] magento/magento2#28561: GraphQL added CORS headers --- .../CorsAllowCredentialsHeaderProvider.php | 17 ++- .../Cors/CorsAllowHeadersHeaderProvider.php | 24 ++-- .../Cors/CorsAllowMethodsHeaderProvider.php | 25 ++-- .../Cors/CorsAllowOriginHeaderProvider.php | 18 ++- .../Cors/CorsMaxAgeHeaderProvider.php | 25 ++-- .../GraphQl/Model/Cors/Configuration.php | 12 +- .../Model/Cors/ConfigurationInterface.php | 10 +- .../Magento/GraphQl/etc/adminhtml/system.xml | 6 + app/code/Magento/GraphQl/etc/di.xml | 25 ++++ .../Magento/GraphQl/CorsHeadersTest.php | 122 ++++++++++++++++++ 10 files changed, 243 insertions(+), 41 deletions(-) create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/CorsHeadersTest.php diff --git a/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowCredentialsHeaderProvider.php b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowCredentialsHeaderProvider.php index 3f7c912b574fc..086cf2bbef877 100644 --- a/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowCredentialsHeaderProvider.php +++ b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowCredentialsHeaderProvider.php @@ -1,13 +1,21 @@ corsConfiguration = $corsConfiguration; + $this->headerName = $headerName; } public function getName() diff --git a/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowHeadersHeaderProvider.php b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowHeadersHeaderProvider.php index e44e7c6b1e872..26df47cb1e312 100644 --- a/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowHeadersHeaderProvider.php +++ b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowHeadersHeaderProvider.php @@ -1,16 +1,21 @@ corsConfiguration = $corsConfiguration; + $this->headerName = $headerName; } public function getName() @@ -36,8 +44,6 @@ public function canApply() : bool public function getValue() { - return $this->corsConfiguration->getAllowedHeaders() - ? $this->corsConfiguration->getAllowedHeaders() - : $this->headerValue; + return $this->corsConfiguration->getAllowedHeaders(); } } diff --git a/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowMethodsHeaderProvider.php b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowMethodsHeaderProvider.php index 548ffc1aec3f6..d2b2994367883 100644 --- a/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowMethodsHeaderProvider.php +++ b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowMethodsHeaderProvider.php @@ -1,17 +1,21 @@ corsConfiguration = $corsConfiguration; + $this->headerName = $headerName; } public function getName() @@ -37,8 +44,6 @@ public function canApply() : bool public function getValue() { - return $this->corsConfiguration->getAllowedMethods() - ? $this->corsConfiguration->getAllowedMethods() - : $this->headerValue; + return $this->corsConfiguration->getAllowedMethods(); } } diff --git a/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowOriginHeaderProvider.php b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowOriginHeaderProvider.php index 8df8c2ec6e39c..0cdc976525a7d 100644 --- a/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowOriginHeaderProvider.php +++ b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowOriginHeaderProvider.php @@ -1,14 +1,21 @@ corsConfiguration = $corsConfiguration; + $this->headerName = $headerName; } public function getName() diff --git a/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsMaxAgeHeaderProvider.php b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsMaxAgeHeaderProvider.php index b74f405930caf..065138dcd7936 100644 --- a/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsMaxAgeHeaderProvider.php +++ b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsMaxAgeHeaderProvider.php @@ -1,17 +1,21 @@ corsConfiguration = $corsConfiguration; + $this->headerName = $headerName; } public function getName() @@ -37,8 +44,6 @@ public function canApply() public function getValue() { - return $this->corsConfiguration->getMaxAge() - ? $this->corsConfiguration->getMaxAge() - : $this->headerValue; + return $this->corsConfiguration->getMaxAge(); } } diff --git a/app/code/Magento/GraphQl/Model/Cors/Configuration.php b/app/code/Magento/GraphQl/Model/Cors/Configuration.php index 6748ea6c3c9a1..cddc3f2ae9653 100644 --- a/app/code/Magento/GraphQl/Model/Cors/Configuration.php +++ b/app/code/Magento/GraphQl/Model/Cors/Configuration.php @@ -1,11 +1,17 @@ scopeConfig->getValue(self::XML_PATH_CORS_MAX_AGE); + return (int) $this->scopeConfig->getValue(self::XML_PATH_CORS_MAX_AGE); } public function isCredentialsAllowed(): bool diff --git a/app/code/Magento/GraphQl/Model/Cors/ConfigurationInterface.php b/app/code/Magento/GraphQl/Model/Cors/ConfigurationInterface.php index bbb23abe854b6..ef298f2d9cfda 100644 --- a/app/code/Magento/GraphQl/Model/Cors/ConfigurationInterface.php +++ b/app/code/Magento/GraphQl/Model/Cors/ConfigurationInterface.php @@ -1,9 +1,15 @@ + The Access-Control-Allow-Origin response header indicates whether the response can be shared with requesting code from the given origin. Fill this field with one or more origins (comma separated) or use '*' to allow access from all origins. 1 @@ -27,6 +28,7 @@ + The Access-Control-Allow-Methods response header specifies the method or methods allowed when accessing the resource in response to a preflight request. Use comma separated methods (e.g. GET,POST) 1 @@ -34,6 +36,7 @@ + The Access-Control-Allow-Headers response header is used in response to a preflight request which includes the Access-Control-Request-Headers to indicate which HTTP headers can be used during the actual request. Use comma separated headers. 1 @@ -41,6 +44,8 @@ + validate-digits + The Access-Control-Max-Age response header indicates how long the results of a preflight request (that is the information contained in the Access-Control-Allow-Methods and Access-Control-Allow-Headers headers) can be cached. 1 @@ -49,6 +54,7 @@ Magento\Config\Model\Config\Source\Yesno + The Access-Control-Allow-Credentials response header tells browsers whether to expose the response to frontend code when the request's credentials mode is include. 1 diff --git a/app/code/Magento/GraphQl/etc/di.xml b/app/code/Magento/GraphQl/etc/di.xml index 79052c717bc96..f0a8eca87ec58 100644 --- a/app/code/Magento/GraphQl/etc/di.xml +++ b/app/code/Magento/GraphQl/etc/di.xml @@ -100,4 +100,29 @@ + + + Access-Control-Max-Age + + + + + Access-Control-Allow-Credentials + + + + + Access-Control-Allow-Headers + + + + + Access-Control-Allow-Methods + + + + + Access-Control-Allow-Origin + + diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/CorsHeadersTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/CorsHeadersTest.php new file mode 100644 index 0000000000000..8110937468280 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/CorsHeadersTest.php @@ -0,0 +1,122 @@ +resourceConfig = $objectManager->get(Config::class); + $this->reinitConfig = $objectManager->get(ReinitableConfigInterface::class); + $this->scopeConfig = $objectManager->get(ScopeConfigInterface::class); + } + + protected function tearDown(): void + { + parent::tearDown(); // TODO: Change the autogenerated stub + + $this->resourceConfig->saveConfig(Configuration::XML_PATH_CORS_HEADERS_ENABLED, 0); + $this->reinitConfig->reinit(); + } + + public function testNoCorsHeadersWhenCorsIsDisabled() + { + $this->resourceConfig->saveConfig(Configuration::XML_PATH_CORS_HEADERS_ENABLED, 0); + $this->resourceConfig->saveConfig(Configuration::XML_PATH_CORS_ALLOWED_HEADERS, 'Origin'); + $this->resourceConfig->saveConfig(Configuration::XML_PATH_CORS_ALLOW_CREDENTIALS, '1'); + $this->resourceConfig->saveConfig(Configuration::XML_PATH_CORS_ALLOWED_METHODS, 'GET,POST'); + $this->resourceConfig->saveConfig(Configuration::XML_PATH_CORS_ALLOWED_ORIGINS, 'magento.local'); + $this->resourceConfig->saveConfig(Configuration::XML_PATH_CORS_MAX_AGE, '86400'); + $this->reinitConfig->reinit(); + + $headers = $this->getHeadersFromIntrospectionQuery(); + + self::assertArrayNotHasKey('Access-Control-Max-Age', $headers); + self::assertArrayNotHasKey('Access-Control-Allow-Credentials', $headers); + self::assertArrayNotHasKey('Access-Control-Allow-Headers', $headers); + self::assertArrayNotHasKey('Access-Control-Allow-Methods', $headers); + self::assertArrayNotHasKey('Access-Control-Allow-Origin', $headers); + } + + public function testCorsHeadersWhenCorsIsEnabled() + { + $this->resourceConfig->saveConfig(Configuration::XML_PATH_CORS_HEADERS_ENABLED, 1); + $this->resourceConfig->saveConfig(Configuration::XML_PATH_CORS_ALLOWED_HEADERS, 'Origin'); + $this->resourceConfig->saveConfig(Configuration::XML_PATH_CORS_ALLOW_CREDENTIALS, '1'); + $this->resourceConfig->saveConfig(Configuration::XML_PATH_CORS_ALLOWED_METHODS, 'GET,POST'); + $this->resourceConfig->saveConfig(Configuration::XML_PATH_CORS_ALLOWED_ORIGINS, 'magento.local'); + $this->resourceConfig->saveConfig(Configuration::XML_PATH_CORS_MAX_AGE, '86400'); + $this->reinitConfig->reinit(); + + $headers = $this->getHeadersFromIntrospectionQuery(); + + self::assertEquals('Origin', $headers['Access-Control-Allow-Headers']); + self::assertEquals('1', $headers['Access-Control-Allow-Credentials']); + self::assertEquals('GET,POST', $headers['Access-Control-Allow-Methods']); + self::assertEquals('magento.local', $headers['Access-Control-Allow-Origin']); + self::assertEquals('86400', $headers['Access-Control-Max-Age']); + } + + public function testEmptyCorsHeadersWhenCorsIsEnabled() + { + $this->resourceConfig->saveConfig(Configuration::XML_PATH_CORS_HEADERS_ENABLED, 1); + $this->resourceConfig->saveConfig(Configuration::XML_PATH_CORS_ALLOWED_HEADERS, ''); + $this->resourceConfig->saveConfig(Configuration::XML_PATH_CORS_ALLOW_CREDENTIALS, ''); + $this->resourceConfig->saveConfig(Configuration::XML_PATH_CORS_ALLOWED_METHODS, ''); + $this->resourceConfig->saveConfig(Configuration::XML_PATH_CORS_ALLOWED_ORIGINS, ''); + $this->resourceConfig->saveConfig(Configuration::XML_PATH_CORS_MAX_AGE, ''); + $this->reinitConfig->reinit(); + + $headers = $this->getHeadersFromIntrospectionQuery(); + + self::assertArrayNotHasKey('Access-Control-Max-Age', $headers); + self::assertArrayNotHasKey('Access-Control-Allow-Credentials', $headers); + self::assertArrayNotHasKey('Access-Control-Allow-Headers', $headers); + self::assertArrayNotHasKey('Access-Control-Allow-Methods', $headers); + self::assertArrayNotHasKey('Access-Control-Allow-Origin', $headers); + } + + private function getHeadersFromIntrospectionQuery() + { + $query + = <<graphQlQueryWithResponseHeaders($query)['headers']; + } +} From 0ac4bb155f2052ac35d52d1b3d404a54a6c6ba64 Mon Sep 17 00:00:00 2001 From: Munkh-Ulzii Balidar Date: Tue, 23 Jun 2020 09:37:38 +0200 Subject: [PATCH 19/28] 28584 revert setting searchresult total count --- .../Model/Resolver/Products/DataProvider/ProductSearch.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php index 298cfd2b0e99c..4c83afb89cc46 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php @@ -110,7 +110,7 @@ public function getList( $searchResults = $this->searchResultsFactory->create(); $searchResults->setSearchCriteria($searchCriteriaForCollection); $searchResults->setItems($collection->getItems()); - $searchResults->setTotalCount($collection->getSize()); + $searchResults->setTotalCount($searchResult->getTotalCount()); return $searchResults; } From 5ba8fd7888ccc2658616fa8897ca9dff58265198 Mon Sep 17 00:00:00 2001 From: Michal Derlatka Date: Tue, 23 Jun 2020 10:23:21 +0200 Subject: [PATCH 20/28] magento/magento2#28561: GraphQL added CORS headers (static test fix) --- .../CorsAllowCredentialsHeaderProvider.php | 24 ++++++++++++- .../Cors/CorsAllowHeadersHeaderProvider.php | 22 ++++++++++++ .../Cors/CorsAllowMethodsHeaderProvider.php | 15 ++++++++ .../Cors/CorsAllowOriginHeaderProvider.php | 15 ++++++++ .../Cors/CorsMaxAgeHeaderProvider.php | 15 ++++++++ .../GraphQl/Model/Cors/Configuration.php | 36 +++++++++++++++++-- .../Model/Cors/ConfigurationInterface.php | 30 ++++++++++++++++ app/code/Magento/GraphQl/etc/config.xml | 6 ++++ .../Magento/GraphQl/CorsHeadersTest.php | 6 +++- 9 files changed, 165 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowCredentialsHeaderProvider.php b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowCredentialsHeaderProvider.php index 086cf2bbef877..39edeb8e6667b 100644 --- a/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowCredentialsHeaderProvider.php +++ b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowCredentialsHeaderProvider.php @@ -15,6 +15,9 @@ */ class CorsAllowCredentialsHeaderProvider implements HeaderProviderInterface { + /** + * @var string + */ private $headerName; /** @@ -24,6 +27,10 @@ class CorsAllowCredentialsHeaderProvider implements HeaderProviderInterface */ private $corsConfiguration; + /** + * @param ConfigurationInterface $corsConfiguration + * @param string $headerName + */ public function __construct( ConfigurationInterface $corsConfiguration, string $headerName @@ -32,16 +39,31 @@ public function __construct( $this->headerName = $headerName; } + /** + * Get name of header + * + * @return string + */ public function getName() { return $this->headerName; } + /** + * Get value for header + * + * @return string + */ public function getValue() { - return true; + return "1"; } + /** + * Check if header can be applied + * + * @return bool + */ public function canApply() : bool { return $this->corsConfiguration->isEnabled() && $this->corsConfiguration->isCredentialsAllowed(); diff --git a/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowHeadersHeaderProvider.php b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowHeadersHeaderProvider.php index 26df47cb1e312..e07cb70644441 100644 --- a/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowHeadersHeaderProvider.php +++ b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowHeadersHeaderProvider.php @@ -15,6 +15,9 @@ */ class CorsAllowHeadersHeaderProvider implements HeaderProviderInterface { + /** + * @var string + */ private $headerName; /** @@ -24,6 +27,10 @@ class CorsAllowHeadersHeaderProvider implements HeaderProviderInterface */ private $corsConfiguration; + /** + * @param ConfigurationInterface $corsConfiguration + * @param string $headerName + */ public function __construct( ConfigurationInterface $corsConfiguration, string $headerName @@ -32,16 +39,31 @@ public function __construct( $this->headerName = $headerName; } + /** + * Get name of header + * + * @return string + */ public function getName() { return $this->headerName; } + /** + * Check if header can be applied + * + * @return bool + */ public function canApply() : bool { return $this->corsConfiguration->isEnabled() && $this->getValue(); } + /** + * Get value for header + * + * @return string + */ public function getValue() { return $this->corsConfiguration->getAllowedHeaders(); diff --git a/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowMethodsHeaderProvider.php b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowMethodsHeaderProvider.php index d2b2994367883..35edca3e90615 100644 --- a/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowMethodsHeaderProvider.php +++ b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowMethodsHeaderProvider.php @@ -32,16 +32,31 @@ public function __construct( $this->headerName = $headerName; } + /** + * Get name of header + * + * @return string + */ public function getName() { return $this->headerName; } + /** + * Check if header can be applied + * + * @return bool + */ public function canApply() : bool { return $this->corsConfiguration->isEnabled() && $this->getValue(); } + /** + * Get value for header + * + * @return string + */ public function getValue() { return $this->corsConfiguration->getAllowedMethods(); diff --git a/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowOriginHeaderProvider.php b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowOriginHeaderProvider.php index 0cdc976525a7d..b6c3641e8580c 100644 --- a/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowOriginHeaderProvider.php +++ b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowOriginHeaderProvider.php @@ -32,16 +32,31 @@ public function __construct( $this->headerName = $headerName; } + /** + * Get name of header + * + * @return string + */ public function getName() { return $this->headerName; } + /** + * Check if header can be applied + * + * @return bool + */ public function canApply() : bool { return $this->corsConfiguration->isEnabled() && $this->getValue(); } + /** + * Get value for header + * + * @return string + */ public function getValue() { return $this->corsConfiguration->getAllowedOrigins(); diff --git a/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsMaxAgeHeaderProvider.php b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsMaxAgeHeaderProvider.php index 065138dcd7936..46a2f44d8ea38 100644 --- a/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsMaxAgeHeaderProvider.php +++ b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsMaxAgeHeaderProvider.php @@ -32,16 +32,31 @@ public function __construct( $this->headerName = $headerName; } + /** + * Get name of header + * + * @return string + */ public function getName() { return $this->headerName; } + /** + * Check if header can be applied + * + * @return bool + */ public function canApply() { return $this->corsConfiguration->isEnabled() && $this->getValue(); } + /** + * Get value for header + * + * @return string + */ public function getValue() { return $this->corsConfiguration->getMaxAge(); diff --git a/app/code/Magento/GraphQl/Model/Cors/Configuration.php b/app/code/Magento/GraphQl/Model/Cors/Configuration.php index cddc3f2ae9653..b06d63832b8d2 100644 --- a/app/code/Magento/GraphQl/Model/Cors/Configuration.php +++ b/app/code/Magento/GraphQl/Model/Cors/Configuration.php @@ -24,41 +24,73 @@ class Configuration implements ConfigurationInterface /** * @var ScopeConfigInterface */ - protected $scopeConfig; + private $scopeConfig; + /** + * @param ScopeConfigInterface $scopeConfig + */ public function __construct(ScopeConfigInterface $scopeConfig) { $this->scopeConfig = $scopeConfig; } + /** + * Are CORS headers enabled + * + * @return bool + */ public function isEnabled(): bool { return $this->scopeConfig->isSetFlag(self::XML_PATH_CORS_HEADERS_ENABLED); } + /** + * Get allowed origins or null if stored configuration is empty + * + * @return string|null + */ public function getAllowedOrigins(): ?string { return $this->scopeConfig->getValue(self::XML_PATH_CORS_ALLOWED_ORIGINS); } + /** + * Get allowed headers or null if stored configuration is empty + * + * @return string|null + */ public function getAllowedHeaders(): ?string { return $this->scopeConfig->getValue(self::XML_PATH_CORS_ALLOWED_HEADERS); } + /** + * Get allowed methods or null if stored configuration is empty + * + * @return string|null + */ public function getAllowedMethods(): ?string { return $this->scopeConfig->getValue(self::XML_PATH_CORS_ALLOWED_METHODS); } + /** + * Get max age header value + * + * @return int + */ public function getMaxAge(): int { return (int) $this->scopeConfig->getValue(self::XML_PATH_CORS_MAX_AGE); } + /** + * Are credentials allowed + * + * @return bool + */ public function isCredentialsAllowed(): bool { return $this->scopeConfig->isSetFlag(self::XML_PATH_CORS_ALLOW_CREDENTIALS); } - } diff --git a/app/code/Magento/GraphQl/Model/Cors/ConfigurationInterface.php b/app/code/Magento/GraphQl/Model/Cors/ConfigurationInterface.php index ef298f2d9cfda..9e54e979323fa 100644 --- a/app/code/Magento/GraphQl/Model/Cors/ConfigurationInterface.php +++ b/app/code/Magento/GraphQl/Model/Cors/ConfigurationInterface.php @@ -12,15 +12,45 @@ */ interface ConfigurationInterface { + /** + * Are CORS headers enabled + * + * @return bool + */ public function isEnabled() : bool; + /** + * Get allowed origins or null if stored configuration is empty + * + * @return string|null + */ public function getAllowedOrigins() : ?string; + /** + * Get allowed headers or null if stored configuration is empty + * + * @return string|null + */ public function getAllowedHeaders() : ?string; + /** + * Get allowed methods or null if stored configuration is empty + * + * @return string|null + */ public function getAllowedMethods() : ?string; + /** + * Get max age header value + * + * @return int + */ public function getMaxAge() : int; + /** + * Are credentials allowed + * + * @return bool + */ public function isCredentialsAllowed() : bool; } diff --git a/app/code/Magento/GraphQl/etc/config.xml b/app/code/Magento/GraphQl/etc/config.xml index 76a1fac199582..39caacbec42d2 100644 --- a/app/code/Magento/GraphQl/etc/config.xml +++ b/app/code/Magento/GraphQl/etc/config.xml @@ -1,4 +1,10 @@ + diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/CorsHeadersTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/CorsHeadersTest.php index 8110937468280..3628d3e4bca32 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/CorsHeadersTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/CorsHeadersTest.php @@ -1,4 +1,9 @@ Date: Tue, 23 Jun 2020 11:50:54 +0200 Subject: [PATCH 21/28] magento/magento2#28561: GraphQL added CORS headers (static test fix) --- .../HttpResponse/Cors/CorsAllowMethodsHeaderProvider.php | 4 ++++ .../HttpResponse/Cors/CorsAllowOriginHeaderProvider.php | 4 ++++ .../Controller/HttpResponse/Cors/CorsMaxAgeHeaderProvider.php | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowMethodsHeaderProvider.php b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowMethodsHeaderProvider.php index 35edca3e90615..654cacfeb4633 100644 --- a/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowMethodsHeaderProvider.php +++ b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowMethodsHeaderProvider.php @@ -24,6 +24,10 @@ class CorsAllowMethodsHeaderProvider implements HeaderProviderInterface */ private $corsConfiguration; + /** + * @param ConfigurationInterface $corsConfiguration + * @param string $headerName + */ public function __construct( ConfigurationInterface $corsConfiguration, string $headerName diff --git a/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowOriginHeaderProvider.php b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowOriginHeaderProvider.php index b6c3641e8580c..7ecc06376ca04 100644 --- a/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowOriginHeaderProvider.php +++ b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowOriginHeaderProvider.php @@ -24,6 +24,10 @@ class CorsAllowOriginHeaderProvider implements HeaderProviderInterface */ private $corsConfiguration; + /** + * @param ConfigurationInterface $corsConfiguration + * @param string $headerName + */ public function __construct( ConfigurationInterface $corsConfiguration, string $headerName diff --git a/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsMaxAgeHeaderProvider.php b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsMaxAgeHeaderProvider.php index 46a2f44d8ea38..7221cd252fab0 100644 --- a/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsMaxAgeHeaderProvider.php +++ b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsMaxAgeHeaderProvider.php @@ -24,6 +24,10 @@ class CorsMaxAgeHeaderProvider implements HeaderProviderInterface */ private $corsConfiguration; + /** + * @param ConfigurationInterface $corsConfiguration + * @param string $headerName + */ public function __construct( ConfigurationInterface $corsConfiguration, string $headerName From 85db11d88a350d01d764efd2bf469cb227f6955b Mon Sep 17 00:00:00 2001 From: Oleksandr Melnyk Date: Tue, 23 Jun 2020 21:01:23 +0300 Subject: [PATCH 22/28] magento/magento2#27952: missing store_name in GraphQL resolver - added store_name --- .../Store/Api/Data/StoreConfigInterface.php | 21 ++++++++++-- .../Magento/Store/Model/Data/StoreConfig.php | 32 ++++++++++++++++--- .../Model/Service/StoreConfigManager.php | 11 ++++++- .../Model/Service/StoreConfigManagerTest.php | 6 ++++ .../Store/StoreConfigDataProvider.php | 13 ++++---- composer.lock | 2 +- .../GraphQl/Store/StoreConfigResolverTest.php | 2 +- 7 files changed, 69 insertions(+), 18 deletions(-) diff --git a/app/code/Magento/Store/Api/Data/StoreConfigInterface.php b/app/code/Magento/Store/Api/Data/StoreConfigInterface.php index 537fec4c75df6..78d7455dc5a92 100644 --- a/app/code/Magento/Store/Api/Data/StoreConfigInterface.php +++ b/app/code/Magento/Store/Api/Data/StoreConfigInterface.php @@ -7,7 +7,7 @@ /** * StoreConfig interface - * + * Interface for store config * @api * @since 100.0.2 */ @@ -141,7 +141,7 @@ public function setWeightUnit($weightUnit); public function getBaseUrl(); /** - * set base URL + * Set base URL * * @param string $baseUrl * @return $this @@ -201,7 +201,7 @@ public function setBaseMediaUrl($baseMediaUrl); public function getSecureBaseUrl(); /** - * set secure base URL + * Set secure base URL * * @param string $secureBaseUrl * @return $this @@ -269,4 +269,19 @@ public function getExtensionAttributes(); public function setExtensionAttributes( \Magento\Store\Api\Data\StoreConfigExtensionInterface $extensionAttributes ); + + /** + * Get store code + * + * @return string + */ + public function getStoreName(); + + /** + * Set store name + * + * @param string $storeName + * @return $this + */ + public function setStoreName(string $storeName); } diff --git a/app/code/Magento/Store/Model/Data/StoreConfig.php b/app/code/Magento/Store/Model/Data/StoreConfig.php index 6634e2cb05bd9..d1e4aa3e25088 100644 --- a/app/code/Magento/Store/Model/Data/StoreConfig.php +++ b/app/code/Magento/Store/Model/Data/StoreConfig.php @@ -7,7 +7,7 @@ /** * Class StoreConfig - * + * Allows to get and set store config values * @codeCoverageIgnore */ class StoreConfig extends \Magento\Framework\Api\AbstractExtensibleObject implements @@ -29,6 +29,7 @@ class StoreConfig extends \Magento\Framework\Api\AbstractExtensibleObject implem const KEY_SECURE_BASE_LINK_URL = 'secure_base_link_url'; const KEY_SECURE_BASE_STATIC_URL = 'secure_base_static_url'; const KEY_SECURE_BASE_MEDIA_URL = 'secure_base_media_url'; + const KEY_STORE_NAME = 'store_name'; /** * Get store id @@ -188,7 +189,7 @@ public function getBaseUrl() } /** - * set base URL + * Set base URL * * @param string $baseUrl * @return $this @@ -293,7 +294,7 @@ public function getSecureBaseUrl() } /** - * set secure base URL + * Set secure base URL * * @param string $secureBaseUrl * @return $this @@ -367,7 +368,7 @@ public function setSecureBaseMediaUrl($secureBaseMediaUrl) } /** - * {@inheritdoc} + * @inheritdoc * * @return \Magento\Store\Api\Data\StoreConfigExtensionInterface|null */ @@ -377,7 +378,7 @@ public function getExtensionAttributes() } /** - * {@inheritdoc} + * @inheritdoc * * @param \Magento\Store\Api\Data\StoreConfigExtensionInterface $extensionAttributes * @return $this @@ -387,4 +388,25 @@ public function setExtensionAttributes( ) { return $this->_setExtensionAttributes($extensionAttributes); } + + /** + * Get store code + * + * @return string + */ + public function getStoreName() + { + return $this->_get(self::KEY_STORE_NAME); + } + + /** + * Set store name + * + * @param string $storeName + * @return $this + */ + public function setStoreName(string $storeName) + { + return $this->setData(self::KEY_STORE_NAME, $storeName); + } } diff --git a/app/code/Magento/Store/Model/Service/StoreConfigManager.php b/app/code/Magento/Store/Model/Service/StoreConfigManager.php index b3c2208a58361..26f4b0e9837c4 100644 --- a/app/code/Magento/Store/Model/Service/StoreConfigManager.php +++ b/app/code/Magento/Store/Model/Service/StoreConfigManager.php @@ -5,6 +5,10 @@ */ namespace Magento\Store\Model\Service; +/** + * Class StoreConfigManager + * Allows to get store config + */ class StoreConfigManager implements \Magento\Store\Api\StoreConfigManagerInterface { /** @@ -53,6 +57,8 @@ public function __construct( } /** + * Get store configs + * * @param string[] $storeCodes list of stores by store codes, will return all if storeCodes is not set * @return \Magento\Store\Api\Data\StoreConfigInterface[] */ @@ -71,6 +77,8 @@ public function getStoreConfigs(array $storeCodes = null) } /** + * Get store config + * * @param \Magento\Store\Model\Store $store * @return \Magento\Store\Api\Data\StoreConfigInterface */ @@ -81,7 +89,8 @@ protected function getStoreConfig($store) $storeConfig->setId($store->getId()) ->setCode($store->getCode()) - ->setWebsiteId($store->getWebsiteId()); + ->setWebsiteId($store->getWebsiteId()) + ->setStoreName($store->getName()); foreach ($this->configPaths as $methodName => $configPath) { $configValue = $this->scopeConfig->getValue( diff --git a/app/code/Magento/Store/Test/Unit/Model/Service/StoreConfigManagerTest.php b/app/code/Magento/Store/Test/Unit/Model/Service/StoreConfigManagerTest.php index c17e2846e22df..51aecb7f39f12 100644 --- a/app/code/Magento/Store/Test/Unit/Model/Service/StoreConfigManagerTest.php +++ b/app/code/Magento/Store/Test/Unit/Model/Service/StoreConfigManagerTest.php @@ -87,6 +87,9 @@ protected function getStoreMock(array $storeConfig) $storeMock->expects($this->once()) ->method('getWebsiteId') ->willReturn($storeConfig['website_id']); + $storeMock->expects($this->any()) + ->method('getName') + ->willReturn($storeConfig['store_name']); $urlMap = [ [UrlInterface::URL_TYPE_WEB, false, $storeConfig['base_url']], @@ -145,6 +148,7 @@ public function testGetStoreConfigs() $baseCurrencyCode = 'USD'; $defaultDisplayCurrencyCode = 'GBP'; $weightUnit = 'lbs'; + $storeName = 'Default Store View'; $storeMocks = []; $storeConfigs = [ @@ -159,6 +163,7 @@ public function testGetStoreConfigs() 'secure_base_static_url' => $secureBaseStaticUrl, 'base_media_url' => $baseMediaUrl, 'secure_base_media_url' => $secureBaseMediaUrl, + 'store_name' => $storeName, ]; $storeMocks[] = $this->getStoreMock($storeConfigs); @@ -205,6 +210,7 @@ public function testGetStoreConfigs() $this->assertEquals($secureBaseStaticUrl, $result[0]->getSecureBaseStaticUrl()); $this->assertEquals($baseMediaUrl, $result[0]->getBaseMediaUrl()); $this->assertEquals($secureBaseMediaUrl, $result[0]->getSecureBaseMediaUrl()); + $this->assertEquals($storeName, $result[0]->getStoreName()); $this->assertEquals($timeZone, $result[0]->getTimezone()); $this->assertEquals($locale, $result[0]->getLocale()); diff --git a/app/code/Magento/StoreGraphQl/Model/Resolver/Store/StoreConfigDataProvider.php b/app/code/Magento/StoreGraphQl/Model/Resolver/Store/StoreConfigDataProvider.php index 59f9831789a35..76b9a12ad0893 100644 --- a/app/code/Magento/StoreGraphQl/Model/Resolver/Store/StoreConfigDataProvider.php +++ b/app/code/Magento/StoreGraphQl/Model/Resolver/Store/StoreConfigDataProvider.php @@ -55,11 +55,10 @@ public function __construct( */ public function getStoreConfigData(StoreInterface $store): array { - $storeConfigData = array_merge( - $this->getBaseConfigData($store), - $this->getExtendedConfigData((int)$store->getId()) + return array_merge( + $this->getExtendedConfigData((int)$store->getId()), + $this->getBaseConfigData($store) ); - return $storeConfigData; } /** @@ -72,7 +71,7 @@ private function getBaseConfigData(StoreInterface $store) : array { $storeConfig = current($this->storeConfigManager->getStoreConfigs([$store->getCode()])); - $storeConfigData = [ + return [ 'id' => $storeConfig->getId(), 'code' => $storeConfig->getCode(), 'website_id' => $storeConfig->getWebsiteId(), @@ -88,9 +87,9 @@ private function getBaseConfigData(StoreInterface $store) : array 'secure_base_url' => $storeConfig->getSecureBaseUrl(), 'secure_base_link_url' => $storeConfig->getSecureBaseLinkUrl(), 'secure_base_static_url' => $storeConfig->getSecureBaseStaticUrl(), - 'secure_base_media_url' => $storeConfig->getSecureBaseMediaUrl() + 'secure_base_media_url' => $storeConfig->getSecureBaseMediaUrl(), + 'store_name' => $storeConfig->getStoreName() ]; - return $storeConfigData; } /** diff --git a/composer.lock b/composer.lock index 6a47e7e44ab69..6a6c945b6416b 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "e86af25d9a4a1942c437cca58f9f1efb", + "content-hash": "f3674961f96b48fdd025a6c94610c8eb", "packages": [ { "name": "colinmollenhour/cache-backend-file", diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Store/StoreConfigResolverTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Store/StoreConfigResolverTest.php index 48619d1392309..6a87788ab09e9 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Store/StoreConfigResolverTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Store/StoreConfigResolverTest.php @@ -91,6 +91,6 @@ public function testGetStoreConfig() $response['storeConfig']['secure_base_static_url'] ); $this->assertEquals($storeConfig->getSecureBaseMediaUrl(), $response['storeConfig']['secure_base_media_url']); - $this->assertEquals('Test Store', $response['storeConfig']['store_name']); + $this->assertEquals($storeConfig->getStoreName(), $response['storeConfig']['store_name']); } } From 096aa37be5ca575276075dfd174490a76ef8a064 Mon Sep 17 00:00:00 2001 From: Oleksandr Melnyk Date: Wed, 24 Jun 2020 13:47:32 +0300 Subject: [PATCH 23/28] magento/magento2#27952: missing store_name in GraphQL resolver - added requested changes --- .../Store/Api/Data/StoreConfigInterface.php | 15 ------------- .../Magento/Store/Model/Data/StoreConfig.php | 22 ------------------- .../Model/Service/StoreConfigManager.php | 3 +-- .../Model/Service/StoreConfigManagerTest.php | 6 ----- .../Store/StoreConfigDataProvider.php | 4 ++-- .../Magento/StoreGraphQl/etc/graphql/di.xml | 7 ------ .../GraphQl/Store/StoreConfigResolverTest.php | 2 +- 7 files changed, 4 insertions(+), 55 deletions(-) diff --git a/app/code/Magento/Store/Api/Data/StoreConfigInterface.php b/app/code/Magento/Store/Api/Data/StoreConfigInterface.php index 78d7455dc5a92..758b24d3bc655 100644 --- a/app/code/Magento/Store/Api/Data/StoreConfigInterface.php +++ b/app/code/Magento/Store/Api/Data/StoreConfigInterface.php @@ -269,19 +269,4 @@ public function getExtensionAttributes(); public function setExtensionAttributes( \Magento\Store\Api\Data\StoreConfigExtensionInterface $extensionAttributes ); - - /** - * Get store code - * - * @return string - */ - public function getStoreName(); - - /** - * Set store name - * - * @param string $storeName - * @return $this - */ - public function setStoreName(string $storeName); } diff --git a/app/code/Magento/Store/Model/Data/StoreConfig.php b/app/code/Magento/Store/Model/Data/StoreConfig.php index d1e4aa3e25088..aa9b48ef198b8 100644 --- a/app/code/Magento/Store/Model/Data/StoreConfig.php +++ b/app/code/Magento/Store/Model/Data/StoreConfig.php @@ -29,7 +29,6 @@ class StoreConfig extends \Magento\Framework\Api\AbstractExtensibleObject implem const KEY_SECURE_BASE_LINK_URL = 'secure_base_link_url'; const KEY_SECURE_BASE_STATIC_URL = 'secure_base_static_url'; const KEY_SECURE_BASE_MEDIA_URL = 'secure_base_media_url'; - const KEY_STORE_NAME = 'store_name'; /** * Get store id @@ -388,25 +387,4 @@ public function setExtensionAttributes( ) { return $this->_setExtensionAttributes($extensionAttributes); } - - /** - * Get store code - * - * @return string - */ - public function getStoreName() - { - return $this->_get(self::KEY_STORE_NAME); - } - - /** - * Set store name - * - * @param string $storeName - * @return $this - */ - public function setStoreName(string $storeName) - { - return $this->setData(self::KEY_STORE_NAME, $storeName); - } } diff --git a/app/code/Magento/Store/Model/Service/StoreConfigManager.php b/app/code/Magento/Store/Model/Service/StoreConfigManager.php index 26f4b0e9837c4..87570d1ff6307 100644 --- a/app/code/Magento/Store/Model/Service/StoreConfigManager.php +++ b/app/code/Magento/Store/Model/Service/StoreConfigManager.php @@ -89,8 +89,7 @@ protected function getStoreConfig($store) $storeConfig->setId($store->getId()) ->setCode($store->getCode()) - ->setWebsiteId($store->getWebsiteId()) - ->setStoreName($store->getName()); + ->setWebsiteId($store->getWebsiteId()); foreach ($this->configPaths as $methodName => $configPath) { $configValue = $this->scopeConfig->getValue( diff --git a/app/code/Magento/Store/Test/Unit/Model/Service/StoreConfigManagerTest.php b/app/code/Magento/Store/Test/Unit/Model/Service/StoreConfigManagerTest.php index 51aecb7f39f12..c17e2846e22df 100644 --- a/app/code/Magento/Store/Test/Unit/Model/Service/StoreConfigManagerTest.php +++ b/app/code/Magento/Store/Test/Unit/Model/Service/StoreConfigManagerTest.php @@ -87,9 +87,6 @@ protected function getStoreMock(array $storeConfig) $storeMock->expects($this->once()) ->method('getWebsiteId') ->willReturn($storeConfig['website_id']); - $storeMock->expects($this->any()) - ->method('getName') - ->willReturn($storeConfig['store_name']); $urlMap = [ [UrlInterface::URL_TYPE_WEB, false, $storeConfig['base_url']], @@ -148,7 +145,6 @@ public function testGetStoreConfigs() $baseCurrencyCode = 'USD'; $defaultDisplayCurrencyCode = 'GBP'; $weightUnit = 'lbs'; - $storeName = 'Default Store View'; $storeMocks = []; $storeConfigs = [ @@ -163,7 +159,6 @@ public function testGetStoreConfigs() 'secure_base_static_url' => $secureBaseStaticUrl, 'base_media_url' => $baseMediaUrl, 'secure_base_media_url' => $secureBaseMediaUrl, - 'store_name' => $storeName, ]; $storeMocks[] = $this->getStoreMock($storeConfigs); @@ -210,7 +205,6 @@ public function testGetStoreConfigs() $this->assertEquals($secureBaseStaticUrl, $result[0]->getSecureBaseStaticUrl()); $this->assertEquals($baseMediaUrl, $result[0]->getBaseMediaUrl()); $this->assertEquals($secureBaseMediaUrl, $result[0]->getSecureBaseMediaUrl()); - $this->assertEquals($storeName, $result[0]->getStoreName()); $this->assertEquals($timeZone, $result[0]->getTimezone()); $this->assertEquals($locale, $result[0]->getLocale()); diff --git a/app/code/Magento/StoreGraphQl/Model/Resolver/Store/StoreConfigDataProvider.php b/app/code/Magento/StoreGraphQl/Model/Resolver/Store/StoreConfigDataProvider.php index 76b9a12ad0893..713179ee2bcca 100644 --- a/app/code/Magento/StoreGraphQl/Model/Resolver/Store/StoreConfigDataProvider.php +++ b/app/code/Magento/StoreGraphQl/Model/Resolver/Store/StoreConfigDataProvider.php @@ -56,8 +56,8 @@ public function __construct( public function getStoreConfigData(StoreInterface $store): array { return array_merge( + $this->getBaseConfigData($store), $this->getExtendedConfigData((int)$store->getId()), - $this->getBaseConfigData($store) ); } @@ -88,7 +88,7 @@ private function getBaseConfigData(StoreInterface $store) : array 'secure_base_link_url' => $storeConfig->getSecureBaseLinkUrl(), 'secure_base_static_url' => $storeConfig->getSecureBaseStaticUrl(), 'secure_base_media_url' => $storeConfig->getSecureBaseMediaUrl(), - 'store_name' => $storeConfig->getStoreName() + 'store_name' => $store->getName() ]; } diff --git a/app/code/Magento/StoreGraphQl/etc/graphql/di.xml b/app/code/Magento/StoreGraphQl/etc/graphql/di.xml index f3771b704c3e9..3a0143821d8b9 100644 --- a/app/code/Magento/StoreGraphQl/etc/graphql/di.xml +++ b/app/code/Magento/StoreGraphQl/etc/graphql/di.xml @@ -23,11 +23,4 @@ - - - - store/information/name - - - diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Store/StoreConfigResolverTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Store/StoreConfigResolverTest.php index 6a87788ab09e9..e7af77f23fa88 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Store/StoreConfigResolverTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Store/StoreConfigResolverTest.php @@ -91,6 +91,6 @@ public function testGetStoreConfig() $response['storeConfig']['secure_base_static_url'] ); $this->assertEquals($storeConfig->getSecureBaseMediaUrl(), $response['storeConfig']['secure_base_media_url']); - $this->assertEquals($storeConfig->getStoreName(), $response['storeConfig']['store_name']); + $this->assertEquals($store->getName(), $response['storeConfig']['store_name']); } } From de9ff7dc57cb1c6b4c882bcb8777e53c62d81663 Mon Sep 17 00:00:00 2001 From: Oleksandr Melnyk Date: Wed, 24 Jun 2020 13:57:24 +0300 Subject: [PATCH 24/28] Update StoreConfigDataProvider.php --- .../Model/Resolver/Store/StoreConfigDataProvider.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/StoreGraphQl/Model/Resolver/Store/StoreConfigDataProvider.php b/app/code/Magento/StoreGraphQl/Model/Resolver/Store/StoreConfigDataProvider.php index 713179ee2bcca..0baee00f468a0 100644 --- a/app/code/Magento/StoreGraphQl/Model/Resolver/Store/StoreConfigDataProvider.php +++ b/app/code/Magento/StoreGraphQl/Model/Resolver/Store/StoreConfigDataProvider.php @@ -57,7 +57,7 @@ public function getStoreConfigData(StoreInterface $store): array { return array_merge( $this->getBaseConfigData($store), - $this->getExtendedConfigData((int)$store->getId()), + $this->getExtendedConfigData((int)$store->getId()) ); } From 42c534c3bcc937f01921281ce6133f9784edd4ac Mon Sep 17 00:00:00 2001 From: Oleksandr Melnyk Date: Wed, 24 Jun 2020 23:18:36 +0300 Subject: [PATCH 25/28] magento/magento2#27952: missing store_name in GraphQL resolver - small amendments with class names --- app/code/Magento/Store/Api/Data/StoreConfigInterface.php | 3 +-- app/code/Magento/Store/Model/Data/StoreConfig.php | 2 +- app/code/Magento/Store/Model/Service/StoreConfigManager.php | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Store/Api/Data/StoreConfigInterface.php b/app/code/Magento/Store/Api/Data/StoreConfigInterface.php index 758b24d3bc655..9fb70d44a6038 100644 --- a/app/code/Magento/Store/Api/Data/StoreConfigInterface.php +++ b/app/code/Magento/Store/Api/Data/StoreConfigInterface.php @@ -6,10 +6,9 @@ namespace Magento\Store\Api\Data; /** - * StoreConfig interface * Interface for store config + * * @api - * @since 100.0.2 */ interface StoreConfigInterface extends \Magento\Framework\Api\ExtensibleDataInterface { diff --git a/app/code/Magento/Store/Model/Data/StoreConfig.php b/app/code/Magento/Store/Model/Data/StoreConfig.php index aa9b48ef198b8..e68d98b162613 100644 --- a/app/code/Magento/Store/Model/Data/StoreConfig.php +++ b/app/code/Magento/Store/Model/Data/StoreConfig.php @@ -6,8 +6,8 @@ namespace Magento\Store\Model\Data; /** - * Class StoreConfig * Allows to get and set store config values + * * @codeCoverageIgnore */ class StoreConfig extends \Magento\Framework\Api\AbstractExtensibleObject implements diff --git a/app/code/Magento/Store/Model/Service/StoreConfigManager.php b/app/code/Magento/Store/Model/Service/StoreConfigManager.php index 87570d1ff6307..debb08438a3b4 100644 --- a/app/code/Magento/Store/Model/Service/StoreConfigManager.php +++ b/app/code/Magento/Store/Model/Service/StoreConfigManager.php @@ -6,7 +6,6 @@ namespace Magento\Store\Model\Service; /** - * Class StoreConfigManager * Allows to get store config */ class StoreConfigManager implements \Magento\Store\Api\StoreConfigManagerInterface From c3e38ac7661bef15976e30197ffacce457d45153 Mon Sep 17 00:00:00 2001 From: Oleksandr Melnyk Date: Wed, 24 Jun 2020 23:22:42 +0300 Subject: [PATCH 26/28] magento/magento2#27952: missing store_name in GraphQL resolver - small amendments with class name --- app/code/Magento/Store/Api/Data/StoreConfigInterface.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Store/Api/Data/StoreConfigInterface.php b/app/code/Magento/Store/Api/Data/StoreConfigInterface.php index 9fb70d44a6038..8f6011f1ae56f 100644 --- a/app/code/Magento/Store/Api/Data/StoreConfigInterface.php +++ b/app/code/Magento/Store/Api/Data/StoreConfigInterface.php @@ -9,6 +9,7 @@ * Interface for store config * * @api + * @since 100.0.2 */ interface StoreConfigInterface extends \Magento\Framework\Api\ExtensibleDataInterface { From 88840a79ad7076a40fd4fe0cf7e5c70d2bd1b2f4 Mon Sep 17 00:00:00 2001 From: Michal Derlatka Date: Thu, 25 Jun 2020 09:50:57 +0200 Subject: [PATCH 27/28] magento/magento2#28561: GraphQL added CORS headers (fixing issues) --- .../Cors/CorsAllowCredentialsHeaderProvider.php | 6 +++--- .../Cors/CorsAllowHeadersHeaderProvider.php | 8 ++++---- .../Cors/CorsAllowMethodsHeaderProvider.php | 11 +++++++---- .../Cors/CorsAllowOriginHeaderProvider.php | 11 +++++++---- .../HttpResponse/Cors/CorsMaxAgeHeaderProvider.php | 13 ++++++++----- .../Magento/GraphQl/Model/Cors/Configuration.php | 12 ++++++------ .../GraphQl/Model/Cors/ConfigurationInterface.php | 10 +++++----- app/code/Magento/GraphQl/etc/di.xml | 10 +++++----- .../testsuite/Magento/GraphQl/CorsHeadersTest.php | 12 ++++++------ 9 files changed, 51 insertions(+), 42 deletions(-) diff --git a/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowCredentialsHeaderProvider.php b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowCredentialsHeaderProvider.php index 39edeb8e6667b..ba2e995d4f704 100644 --- a/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowCredentialsHeaderProvider.php +++ b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowCredentialsHeaderProvider.php @@ -44,7 +44,7 @@ public function __construct( * * @return string */ - public function getName() + public function getName(): string { return $this->headerName; } @@ -54,7 +54,7 @@ public function getName() * * @return string */ - public function getValue() + public function getValue(): string { return "1"; } @@ -64,7 +64,7 @@ public function getValue() * * @return bool */ - public function canApply() : bool + public function canApply(): bool { return $this->corsConfiguration->isEnabled() && $this->corsConfiguration->isCredentialsAllowed(); } diff --git a/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowHeadersHeaderProvider.php b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowHeadersHeaderProvider.php index e07cb70644441..68760de543daa 100644 --- a/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowHeadersHeaderProvider.php +++ b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowHeadersHeaderProvider.php @@ -44,7 +44,7 @@ public function __construct( * * @return string */ - public function getName() + public function getName(): string { return $this->headerName; } @@ -54,7 +54,7 @@ public function getName() * * @return bool */ - public function canApply() : bool + public function canApply(): bool { return $this->corsConfiguration->isEnabled() && $this->getValue(); } @@ -62,9 +62,9 @@ public function canApply() : bool /** * Get value for header * - * @return string + * @return string|null */ - public function getValue() + public function getValue(): ?string { return $this->corsConfiguration->getAllowedHeaders(); } diff --git a/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowMethodsHeaderProvider.php b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowMethodsHeaderProvider.php index 654cacfeb4633..233839b9deb74 100644 --- a/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowMethodsHeaderProvider.php +++ b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowMethodsHeaderProvider.php @@ -15,6 +15,9 @@ */ class CorsAllowMethodsHeaderProvider implements HeaderProviderInterface { + /** + * @var string + */ private $headerName; /** @@ -41,7 +44,7 @@ public function __construct( * * @return string */ - public function getName() + public function getName(): string { return $this->headerName; } @@ -51,7 +54,7 @@ public function getName() * * @return bool */ - public function canApply() : bool + public function canApply(): bool { return $this->corsConfiguration->isEnabled() && $this->getValue(); } @@ -59,9 +62,9 @@ public function canApply() : bool /** * Get value for header * - * @return string + * @return string|null */ - public function getValue() + public function getValue(): ?string { return $this->corsConfiguration->getAllowedMethods(); } diff --git a/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowOriginHeaderProvider.php b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowOriginHeaderProvider.php index 7ecc06376ca04..21850f18db1f2 100644 --- a/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowOriginHeaderProvider.php +++ b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowOriginHeaderProvider.php @@ -15,6 +15,9 @@ */ class CorsAllowOriginHeaderProvider implements HeaderProviderInterface { + /** + * @var string + */ private $headerName; /** @@ -41,7 +44,7 @@ public function __construct( * * @return string */ - public function getName() + public function getName(): string { return $this->headerName; } @@ -51,7 +54,7 @@ public function getName() * * @return bool */ - public function canApply() : bool + public function canApply(): bool { return $this->corsConfiguration->isEnabled() && $this->getValue(); } @@ -59,9 +62,9 @@ public function canApply() : bool /** * Get value for header * - * @return string + * @return string|null */ - public function getValue() + public function getValue(): ?string { return $this->corsConfiguration->getAllowedOrigins(); } diff --git a/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsMaxAgeHeaderProvider.php b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsMaxAgeHeaderProvider.php index 7221cd252fab0..e30209ae25e68 100644 --- a/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsMaxAgeHeaderProvider.php +++ b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsMaxAgeHeaderProvider.php @@ -15,6 +15,9 @@ */ class CorsMaxAgeHeaderProvider implements HeaderProviderInterface { + /** + * @var string + */ private $headerName; /** @@ -41,7 +44,7 @@ public function __construct( * * @return string */ - public function getName() + public function getName(): string { return $this->headerName; } @@ -51,7 +54,7 @@ public function getName() * * @return bool */ - public function canApply() + public function canApply(): bool { return $this->corsConfiguration->isEnabled() && $this->getValue(); } @@ -59,10 +62,10 @@ public function canApply() /** * Get value for header * - * @return string + * @return string|null */ - public function getValue() + public function getValue(): ?string { - return $this->corsConfiguration->getMaxAge(); + return (string) $this->corsConfiguration->getMaxAge(); } } diff --git a/app/code/Magento/GraphQl/Model/Cors/Configuration.php b/app/code/Magento/GraphQl/Model/Cors/Configuration.php index b06d63832b8d2..dd5a0b426e22d 100644 --- a/app/code/Magento/GraphQl/Model/Cors/Configuration.php +++ b/app/code/Magento/GraphQl/Model/Cors/Configuration.php @@ -14,12 +14,12 @@ */ class Configuration implements ConfigurationInterface { - const XML_PATH_CORS_HEADERS_ENABLED = 'graphql/cors/enabled'; - const XML_PATH_CORS_ALLOWED_ORIGINS = 'graphql/cors/allowed_origins'; - const XML_PATH_CORS_ALLOWED_HEADERS = 'graphql/cors/allowed_headers'; - const XML_PATH_CORS_ALLOWED_METHODS = 'graphql/cors/allowed_methods'; - const XML_PATH_CORS_MAX_AGE = 'graphql/cors/max_age'; - const XML_PATH_CORS_ALLOW_CREDENTIALS = 'graphql/cors/allow_credentials'; + public const XML_PATH_CORS_HEADERS_ENABLED = 'graphql/cors/enabled'; + public const XML_PATH_CORS_ALLOWED_ORIGINS = 'graphql/cors/allowed_origins'; + public const XML_PATH_CORS_ALLOWED_HEADERS = 'graphql/cors/allowed_headers'; + public const XML_PATH_CORS_ALLOWED_METHODS = 'graphql/cors/allowed_methods'; + public const XML_PATH_CORS_MAX_AGE = 'graphql/cors/max_age'; + public const XML_PATH_CORS_ALLOW_CREDENTIALS = 'graphql/cors/allow_credentials'; /** * @var ScopeConfigInterface diff --git a/app/code/Magento/GraphQl/Model/Cors/ConfigurationInterface.php b/app/code/Magento/GraphQl/Model/Cors/ConfigurationInterface.php index 9e54e979323fa..b40b64f48e51f 100644 --- a/app/code/Magento/GraphQl/Model/Cors/ConfigurationInterface.php +++ b/app/code/Magento/GraphQl/Model/Cors/ConfigurationInterface.php @@ -17,35 +17,35 @@ interface ConfigurationInterface * * @return bool */ - public function isEnabled() : bool; + public function isEnabled(): bool; /** * Get allowed origins or null if stored configuration is empty * * @return string|null */ - public function getAllowedOrigins() : ?string; + public function getAllowedOrigins(): ?string; /** * Get allowed headers or null if stored configuration is empty * * @return string|null */ - public function getAllowedHeaders() : ?string; + public function getAllowedHeaders(): ?string; /** * Get allowed methods or null if stored configuration is empty * * @return string|null */ - public function getAllowedMethods() : ?string; + public function getAllowedMethods(): ?string; /** * Get max age header value * * @return int */ - public function getMaxAge() : int; + public function getMaxAge(): int; /** * Are credentials allowed diff --git a/app/code/Magento/GraphQl/etc/di.xml b/app/code/Magento/GraphQl/etc/di.xml index f0a8eca87ec58..fca6c425e2507 100644 --- a/app/code/Magento/GraphQl/etc/di.xml +++ b/app/code/Magento/GraphQl/etc/di.xml @@ -100,27 +100,27 @@ - + Access-Control-Max-Age - + Access-Control-Allow-Credentials - + Access-Control-Allow-Headers - + Access-Control-Allow-Methods - + Access-Control-Allow-Origin diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/CorsHeadersTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/CorsHeadersTest.php index 3628d3e4bca32..25c808a549e80 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/CorsHeadersTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/CorsHeadersTest.php @@ -45,13 +45,13 @@ protected function setUp(): void protected function tearDown(): void { - parent::tearDown(); // TODO: Change the autogenerated stub + parent::tearDown(); $this->resourceConfig->saveConfig(Configuration::XML_PATH_CORS_HEADERS_ENABLED, 0); $this->reinitConfig->reinit(); } - public function testNoCorsHeadersWhenCorsIsDisabled() + public function testNoCorsHeadersWhenCorsIsDisabled(): void { $this->resourceConfig->saveConfig(Configuration::XML_PATH_CORS_HEADERS_ENABLED, 0); $this->resourceConfig->saveConfig(Configuration::XML_PATH_CORS_ALLOWED_HEADERS, 'Origin'); @@ -70,7 +70,7 @@ public function testNoCorsHeadersWhenCorsIsDisabled() self::assertArrayNotHasKey('Access-Control-Allow-Origin', $headers); } - public function testCorsHeadersWhenCorsIsEnabled() + public function testCorsHeadersWhenCorsIsEnabled(): void { $this->resourceConfig->saveConfig(Configuration::XML_PATH_CORS_HEADERS_ENABLED, 1); $this->resourceConfig->saveConfig(Configuration::XML_PATH_CORS_ALLOWED_HEADERS, 'Origin'); @@ -89,7 +89,7 @@ public function testCorsHeadersWhenCorsIsEnabled() self::assertEquals('86400', $headers['Access-Control-Max-Age']); } - public function testEmptyCorsHeadersWhenCorsIsEnabled() + public function testEmptyCorsHeadersWhenCorsIsEnabled(): void { $this->resourceConfig->saveConfig(Configuration::XML_PATH_CORS_HEADERS_ENABLED, 1); $this->resourceConfig->saveConfig(Configuration::XML_PATH_CORS_ALLOWED_HEADERS, ''); @@ -108,7 +108,7 @@ public function testEmptyCorsHeadersWhenCorsIsEnabled() self::assertArrayNotHasKey('Access-Control-Allow-Origin', $headers); } - private function getHeadersFromIntrospectionQuery() + private function getHeadersFromIntrospectionQuery(): array { $query = <<graphQlQueryWithResponseHeaders($query)['headers']; + return $this->graphQlQueryWithResponseHeaders($query)['headers'] ?? []; } } From 6f918fa450580d623454f7be36d439def9cf6779 Mon Sep 17 00:00:00 2001 From: Alex Taranovsky Date: Wed, 24 Jun 2020 15:47:52 +0300 Subject: [PATCH 28/28] magento/magento2#28481: GraphQl. updateCustomer allows to set any INT value in gender argument --- .../Api/ValidateCustomerDataInterface.php | 24 +++++ .../Model/Customer/ValidateCustomerData.php | 97 +++---------------- .../ValidateCustomerData/ValidateEmail.php | 45 +++++++++ .../ValidateCustomerData/ValidateGender.php | 58 +++++++++++ .../ValidateRequiredArguments.php | 59 +++++++++++ .../CustomerGraphQl/etc/graphql/di.xml | 10 ++ 6 files changed, 209 insertions(+), 84 deletions(-) create mode 100644 app/code/Magento/CustomerGraphQl/Api/ValidateCustomerDataInterface.php create mode 100644 app/code/Magento/CustomerGraphQl/Model/Customer/ValidateCustomerData/ValidateEmail.php create mode 100644 app/code/Magento/CustomerGraphQl/Model/Customer/ValidateCustomerData/ValidateGender.php create mode 100644 app/code/Magento/CustomerGraphQl/Model/Customer/ValidateCustomerData/ValidateRequiredArguments.php diff --git a/app/code/Magento/CustomerGraphQl/Api/ValidateCustomerDataInterface.php b/app/code/Magento/CustomerGraphQl/Api/ValidateCustomerDataInterface.php new file mode 100644 index 0000000000000..ef3e86788c43f --- /dev/null +++ b/app/code/Magento/CustomerGraphQl/Api/ValidateCustomerDataInterface.php @@ -0,0 +1,24 @@ +getAllowedCustomerAttributes = $getAllowedCustomerAttributes; $this->emailAddressValidator = $emailAddressValidator; - $this->customerMetadata = $customerMetadata; + $this->validators = $validators; } /** @@ -61,81 +60,11 @@ public function __construct( * @throws LocalizedException * @throws NoSuchEntityException */ - public function execute(array $customerData): void - { - $this->validateRequiredArguments($customerData); - $this->validateEmail($customerData); - $this->validateGender($customerData); - } - - /** - * Validate required attributes - * - * @param array $customerData - * @throws GraphQlInputException - */ - private function validateRequiredArguments(array $customerData): void + public function execute(array $customerData) { - $attributes = $this->getAllowedCustomerAttributes->execute(array_keys($customerData)); - $errorInput = []; - - foreach ($attributes as $attributeInfo) { - if ($attributeInfo->getIsRequired() - && (!isset($customerData[$attributeInfo->getAttributeCode()]) - || $customerData[$attributeInfo->getAttributeCode()] == '') - ) { - $errorInput[] = $attributeInfo->getDefaultFrontendLabel(); - } - } - - if ($errorInput) { - throw new GraphQlInputException( - __('Required parameters are missing: %1', [implode(', ', $errorInput)]) - ); - } - } - - /** - * Validate an email - * - * @param array $customerData - * @throws GraphQlInputException - */ - private function validateEmail(array $customerData): void - { - if (isset($customerData['email']) && !$this->emailAddressValidator->isValid($customerData['email'])) { - throw new GraphQlInputException( - __('"%1" is not a valid email address.', $customerData['email']) - ); - } - } - - /** - * Validate gender value - * - * @param array $customerData - * @throws GraphQlInputException - * @throws LocalizedException - * @throws NoSuchEntityException - */ - private function validateGender(array $customerData): void - { - if (isset($customerData['gender']) && $customerData['gender']) { - /** @var AttributeMetadata $genderData */ - $options = $this->customerMetadata->getAttributeMetadata('gender')->getOptions(); - - $isValid = false; - foreach ($options as $optionData) { - if ($optionData->getValue() && $optionData->getValue() == $customerData['gender']) { - $isValid = true; - } - } - - if (!$isValid) { - throw new GraphQlInputException( - __('"%1" is not a valid gender value.', $customerData['gender']) - ); - } + /** @var ValidateCustomerDataInterface $validator */ + foreach ($this->validators as $validator) { + $validator->execute($customerData); } } } diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/ValidateCustomerData/ValidateEmail.php b/app/code/Magento/CustomerGraphQl/Model/Customer/ValidateCustomerData/ValidateEmail.php new file mode 100644 index 0000000000000..87f8831550f04 --- /dev/null +++ b/app/code/Magento/CustomerGraphQl/Model/Customer/ValidateCustomerData/ValidateEmail.php @@ -0,0 +1,45 @@ +emailAddressValidator = $emailAddressValidator; + } + + /** + * @inheritDoc + */ + public function execute(array $customerData): void + { + if (isset($customerData['email']) && !$this->emailAddressValidator->isValid($customerData['email'])) { + throw new GraphQlInputException( + __('"%1" is not a valid email address.', $customerData['email']) + ); + } + } +} diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/ValidateCustomerData/ValidateGender.php b/app/code/Magento/CustomerGraphQl/Model/Customer/ValidateCustomerData/ValidateGender.php new file mode 100644 index 0000000000000..463372a63d8d5 --- /dev/null +++ b/app/code/Magento/CustomerGraphQl/Model/Customer/ValidateCustomerData/ValidateGender.php @@ -0,0 +1,58 @@ +customerMetadata = $customerMetadata; + } + + /** + * @inheritDoc + */ + public function execute(array $customerData): void + { + if (isset($customerData['gender']) && $customerData['gender']) { + /** @var AttributeMetadata $genderData */ + $options = $this->customerMetadata->getAttributeMetadata('gender')->getOptions(); + + $isValid = false; + foreach ($options as $optionData) { + if ($optionData->getValue() && $optionData->getValue() == $customerData['gender']) { + $isValid = true; + } + } + + if (!$isValid) { + throw new GraphQlInputException( + __('"%1" is not a valid gender value.', $customerData['gender']) + ); + } + } + } +} diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/ValidateCustomerData/ValidateRequiredArguments.php b/app/code/Magento/CustomerGraphQl/Model/Customer/ValidateCustomerData/ValidateRequiredArguments.php new file mode 100644 index 0000000000000..fdf4fa1c824f2 --- /dev/null +++ b/app/code/Magento/CustomerGraphQl/Model/Customer/ValidateCustomerData/ValidateRequiredArguments.php @@ -0,0 +1,59 @@ +getAllowedCustomerAttributes = $getAllowedCustomerAttributes; + } + + /** + * @inheritDoc + */ + public function execute(array $customerData): void + { + $attributes = $this->getAllowedCustomerAttributes->execute(array_keys($customerData)); + $errorInput = []; + + foreach ($attributes as $attributeInfo) { + if ($attributeInfo->getIsRequired() + && (!isset($customerData[$attributeInfo->getAttributeCode()]) + || $customerData[$attributeInfo->getAttributeCode()] == '') + ) { + $errorInput[] = $attributeInfo->getDefaultFrontendLabel(); + } + } + + if ($errorInput) { + throw new GraphQlInputException( + __('Required parameters are missing: %1', [implode(', ', $errorInput)]) + ); + } + } +} diff --git a/app/code/Magento/CustomerGraphQl/etc/graphql/di.xml b/app/code/Magento/CustomerGraphQl/etc/graphql/di.xml index 1ba0e457430e0..3ed77a2ad563c 100644 --- a/app/code/Magento/CustomerGraphQl/etc/graphql/di.xml +++ b/app/code/Magento/CustomerGraphQl/etc/graphql/di.xml @@ -29,4 +29,14 @@ + + + + + Magento\CustomerGraphQl\Model\Customer\ValidateCustomerData\ValidateRequiredArguments + Magento\CustomerGraphQl\Model\Customer\ValidateCustomerData\ValidateEmail + Magento\CustomerGraphQl\Model\Customer\ValidateCustomerData\ValidateGender + + +