Skip to content

Commit 52142d5

Browse files
🔃 [Magento Community Engineering] Community Contributions - GraphQL
Accepted Community Pull Requests:
2 parents fcd419f + 173038b commit 52142d5

19 files changed

+520
-56
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ Welcome to Magento 2 installation! We're glad you chose to install Magento 2, a
1111

1212
* [Installation Guide](https://devdocs.magento.com/guides/v2.3/install-gde/bk-install-guide.html).
1313

14+
## Learn More About GraphQL in Magento 2
15+
16+
* [GraphQL Developer Guide](https://devdocs.magento.com/guides/v2.3/graphql/index.html)
17+
1418
<h2>Contributing to the Magento 2 Code Base</h2>
1519
Contributions can take the form of new components or features, changes to existing features, tests, documentation (such as developer guides, user guides, examples, or specifications), bug fixes, optimizations, or just good suggestions.
1620

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ input PaymentMethodInput {
1111
}
1212

1313
input BraintreeInput {
14-
payment_method_nonce: String!
15-
is_active_payment_token_enabler: Boolean!
16-
device_data: String
14+
payment_method_nonce: String! @doc(description:"The one-time payment token generated by Braintree payment gateway based on card details. Required field to make sale transaction.")
15+
is_active_payment_token_enabler: Boolean! @doc(description:"States whether an entered by a customer credit/debit card should be tokenized for later usage. Required only if Vault is enabled for Braintree payment integration.")
16+
device_data: String @doc(description:"Contains a fingerprint provided by Braintree JS SDK and should be sent with sale transaction details to the Braintree payment gateway. Should be specified only in a case if Kount (advanced fraud protection) is enabled for Braintree payment integration.")
1717
}
1818

1919
input BraintreeCcVaultInput {

app/code/Magento/CustomerGraphQl/Model/Customer/Address/ExtractCustomerAddressData.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,8 @@ public function execute(AddressInterface $address): array
125125
}
126126
$addressData = array_merge($addressData, $customAttributes);
127127

128+
$addressData['customer_id'] = null;
129+
128130
return $addressData;
129131
}
130132
}

app/code/Magento/CustomerGraphQl/Model/Customer/ExtractCustomerData.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,11 @@ public function execute(CustomerInterface $customer): array
101101
}
102102
}
103103
$customerData = array_merge($customerData, $customAttributes);
104-
104+
//Field is deprecated and should not be exposed on storefront.
105+
$customerData['group_id'] = null;
105106
$customerData['model'] = $customer;
107+
$customerData['id'] = null;
108+
106109
return $customerData;
107110
}
108111
}

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

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,13 @@ input CustomerAddressInput {
3636
prefix: String @doc(description: "An honorific, such as Dr., Mr., or Mrs.")
3737
suffix: String @doc(description: "A value such as Sr., Jr., or III")
3838
vat_id: String @doc(description: "The customer's Tax/VAT number (for corporate customers)")
39-
custom_attributes: [CustomerAddressAttributeInput] @doc(description: "Address custom attributes")
39+
custom_attributes: [CustomerAddressAttributeInput] @doc(description: "Deprecated: Custom attributes should not be put into container.")
4040
}
4141

4242
input CustomerAddressRegionInput @doc(description: "CustomerAddressRegionInput defines the customer's state or province") {
4343
region_code: String @doc(description: "The address region code")
4444
region: String @doc(description: "The state or province name")
45-
region_id: Int @doc(description: "Uniquely identifies the region")
45+
region_id: Int @doc(description: "region_id is deprecated. Region ID is excessive on storefront and region code should suffice for all scenarios")
4646
}
4747

4848
input CustomerAddressAttributeInput {
@@ -78,7 +78,7 @@ type RevokeCustomerTokenOutput {
7878

7979
type Customer @doc(description: "Customer defines the customer name and address and other details") {
8080
created_at: String @doc(description: "Timestamp indicating when the account was created")
81-
group_id: Int @doc(description: "The group assigned to the user. Default values are 0 (Not logged in), 1 (General), 2 (Wholesale), and 3 (Retailer)")
81+
group_id: Int @deprecated(reason: "Customer group should not be exposed in the storefront scenarios")
8282
prefix: String @doc(description: "An honorific, such as Dr., Mr., or Mrs.")
8383
firstname: String @doc(description: "The customer's first name")
8484
middlename: String @doc(description: "The customer's middle name")
@@ -89,17 +89,17 @@ type Customer @doc(description: "Customer defines the customer name and address
8989
default_shipping: String @doc(description: "The ID assigned to the shipping address")
9090
dob: String @doc(description: "The customer's date of birth")
9191
taxvat: String @doc(description: "The customer's Tax/VAT number (for corporate customers)")
92-
id: Int @doc(description: "The ID assigned to the customer")
92+
id: Int @doc(description: "The ID assigned to the customer") @deprecated(reason: "id is not needed as part of Customer because on server side it can be identified based on customer token used for authentication. There is no need to know customer ID on the client side.")
9393
is_subscribed: Boolean @doc(description: "Indicates whether the customer is subscribed to the company's newsletter") @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\IsSubscribed")
9494
addresses: [CustomerAddress] @doc(description: "An array containing the customer's shipping and billing addresses") @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\CustomerAddresses")
9595
gender: Int @doc(description: "The customer's gender(Male - 1, Female - 2)")
9696
}
9797

9898
type CustomerAddress @doc(description: "CustomerAddress contains detailed information about a customer's billing and shipping addresses"){
9999
id: Int @doc(description: "The ID assigned to the address object")
100-
customer_id: Int @doc(description: "The customer ID")
100+
customer_id: Int @doc(description: "The customer ID") @deprecated(reason: "customer_id is not needed as part of CustomerAddress, address ID (id) is unique identifier for the addresses.")
101101
region: CustomerAddressRegion @doc(description: "An object containing the region name, region code, and region ID")
102-
region_id: Int @doc(description: "A number that uniquely identifies the state, province, or other area")
102+
region_id: Int @deprecated(reason: "Region ID is excessive on storefront and region code should suffice for all scenarios")
103103
country_id: String @doc(description: "The customer's country")
104104
street: [String] @doc(description: "An array of strings that define the street number and name")
105105
company: String @doc(description: "The customer's company")
@@ -115,14 +115,14 @@ type CustomerAddress @doc(description: "CustomerAddress contains detailed inform
115115
vat_id: String @doc(description: "The customer's Tax/VAT number (for corporate customers)")
116116
default_shipping: Boolean @doc(description: "Indicates whether the address is the default shipping address")
117117
default_billing: Boolean @doc(description: "Indicates whether the address is the default billing address")
118-
custom_attributes: [CustomerAddressAttribute] @doc(description: "Address custom attributes")
118+
custom_attributes: [CustomerAddressAttribute] @deprecated(reason: "Custom attributes should not be put into container")
119119
extension_attributes: [CustomerAddressAttribute] @doc(description: "Address extension attributes")
120120
}
121121

122122
type CustomerAddressRegion @doc(description: "CustomerAddressRegion defines the customer's state or province") {
123123
region_code: String @doc(description: "The address region code")
124124
region: String @doc(description: "The state or province name")
125-
region_id: Int @doc(description: "Uniquely identifies the region")
125+
region_id: Int @deprecated(reason: "Region ID is excessive on storefront and region code should suffice for all scenarios")
126126
}
127127

128128
type CustomerAddressAttribute {

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

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -31,36 +31,37 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value
3131
/** @var Address $address */
3232
$address = $value['model'];
3333
$rates = $address->getAllShippingRates();
34-
$carrierTitle = null;
35-
$methodTitle = null;
34+
$carrierTitle = '';
35+
$methodTitle = '';
3636

37-
if (count($rates) > 0 && !empty($address->getShippingMethod())) {
38-
list($carrierCode, $methodCode) = explode('_', $address->getShippingMethod(), 2);
37+
if (!count($rates) || empty($address->getShippingMethod())) {
38+
return null;
39+
}
3940

40-
/** @var Rate $rate */
41-
foreach ($rates as $rate) {
42-
if ($rate->getCode() == $address->getShippingMethod()) {
43-
$carrierTitle = $rate->getCarrierTitle();
44-
$methodTitle = $rate->getMethodTitle();
45-
break;
46-
}
47-
}
41+
list($carrierCode, $methodCode) = explode('_', $address->getShippingMethod(), 2);
4842

49-
$data = [
50-
'carrier_code' => $carrierCode,
51-
'method_code' => $methodCode,
52-
'carrier_title' => $carrierTitle,
53-
'method_title' => $methodTitle,
54-
'amount' => [
55-
'value' => $address->getShippingAmount(),
56-
'currency' => $address->getQuote()->getQuoteCurrencyCode(),
57-
],
58-
/** @deprecated The field should not be used on the storefront */
59-
'base_amount' => null,
60-
];
61-
} else {
62-
$data = null;
43+
/** @var Rate $rate */
44+
foreach ($rates as $rate) {
45+
if ($rate->getCode() == $address->getShippingMethod()) {
46+
$carrierTitle = $rate->getCarrierTitle();
47+
$methodTitle = $rate->getMethodTitle();
48+
break;
49+
}
6350
}
51+
52+
$data = [
53+
'carrier_code' => $carrierCode,
54+
'method_code' => $methodCode,
55+
'carrier_title' => $carrierTitle,
56+
'method_title' => $methodTitle,
57+
'amount' => [
58+
'value' => $address->getShippingAmount(),
59+
'currency' => $address->getQuote()->getQuoteCurrencyCode(),
60+
],
61+
/** @deprecated The field should not be used on the storefront */
62+
'base_amount' => null,
63+
];
64+
6465
return $data;
6566
}
6667
}

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -244,11 +244,11 @@ type CartAddressCountry {
244244
}
245245

246246
type SelectedShippingMethod {
247-
carrier_code: String
248-
method_code: String
249-
carrier_title: String
250-
method_title: String
251-
amount: Money
247+
carrier_code: String!
248+
method_code: String!
249+
carrier_title: String!
250+
method_title: String!
251+
amount: Money!
252252
base_amount: Money @deprecated(reason: "The field should not be used on the storefront")
253253
}
254254

dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/AddConfigurableProductToCartTest.php

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
namespace Magento\GraphQl\ConfigurableProduct;
99

10+
use Exception;
1011
use Magento\GraphQl\Quote\GetMaskedQuoteIdByReservedOrderId;
1112
use Magento\TestFramework\Helper\Bootstrap;
1213
use Magento\TestFramework\TestCase\GraphQlAbstract;
@@ -139,6 +140,67 @@ public function testAddMultipleConfigurableProductToCart()
139140
}
140141
}
141142

143+
/**
144+
* @magentoApiDataFixture Magento/ConfigurableProduct/_files/configurable_products.php
145+
* @magentoApiDataFixture Magento/Checkout/_files/active_quote.php
146+
*
147+
* @expectedException Exception
148+
* @expectedExceptionMessage You need to choose options for your item.
149+
*/
150+
public function testAddVariationFromAnotherConfigurableProductWithTheSameSuperAttributeToCart()
151+
{
152+
$this->markTestSkipped(
153+
'Magento automatically selects the correct child product according to the super attribute
154+
https://github.com/magento/graphql-ce/issues/940'
155+
);
156+
157+
$searchResponse = $this->graphQlQuery($this->getFetchProductQuery('configurable_12345'));
158+
$product = current($searchResponse['products']['items']);
159+
160+
$quantity = 2;
161+
$maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1');
162+
$parentSku = $product['sku'];
163+
164+
$sku = 'simple_20';
165+
166+
$query = $this->getQuery(
167+
$maskedQuoteId,
168+
$parentSku,
169+
$sku,
170+
$quantity
171+
);
172+
173+
$this->graphQlMutation($query);
174+
}
175+
176+
/**
177+
* @magentoApiDataFixture Magento/ConfigurableProduct/_files/configurable_products_with_different_super_attribute.php
178+
* @magentoApiDataFixture Magento/Checkout/_files/active_quote.php
179+
*
180+
* @expectedException Exception
181+
* @expectedExceptionMessage You need to choose options for your item.
182+
*/
183+
public function testAddVariationFromAnotherConfigurableProductWithDifferentSuperAttributeToCart()
184+
{
185+
$searchResponse = $this->graphQlQuery($this->getFetchProductQuery('configurable_12345'));
186+
$product = current($searchResponse['products']['items']);
187+
188+
$quantity = 2;
189+
$maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1');
190+
$parentSku = $product['sku'];
191+
192+
$sku = 'simple_20';
193+
194+
$query = $this->getQuery(
195+
$maskedQuoteId,
196+
$parentSku,
197+
$sku,
198+
$quantity
199+
);
200+
201+
$this->graphQlMutation($query);
202+
}
203+
142204
/**
143205
* @magentoApiDataFixture Magento/ConfigurableProduct/_files/product_configurable_sku.php
144206
* @magentoApiDataFixture Magento/Checkout/_files/active_quote.php
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
namespace Magento\GraphQl\ConfigurableProduct;
10+
11+
use Magento\ConfigurableProduct\Model\Product\Type\Configurable;
12+
use Magento\Framework\Exception\NoSuchEntityException as NoSuchEntityException;
13+
use Magento\GraphQl\Quote\GetMaskedQuoteIdByReservedOrderId;
14+
use Magento\Quote\Model\Quote\Item;
15+
use Magento\Quote\Model\QuoteFactory;
16+
use Magento\Quote\Model\QuoteIdMaskFactory;
17+
use Magento\Quote\Model\ResourceModel\Quote as QuoteResource;
18+
use Magento\TestFramework\Helper\Bootstrap;
19+
use Magento\TestFramework\TestCase\GraphQlAbstract;
20+
21+
/**
22+
* checks that qty of configurable product is updated in cart
23+
*/
24+
class UpdateConfigurableCartItemsTest extends GraphQlAbstract
25+
{
26+
/**
27+
* @var QuoteIdMaskFactory
28+
*/
29+
protected $quoteIdMaskFactory;
30+
31+
/**
32+
* @var GetMaskedQuoteIdByReservedOrderId
33+
*/
34+
private $getMaskedQuoteIdByReservedOrderId;
35+
36+
/**
37+
* @var QuoteFactory
38+
*/
39+
private $quoteFactory;
40+
41+
/**
42+
* @var QuoteResource
43+
*/
44+
private $quoteResource;
45+
46+
/**
47+
* @magentoApiDataFixture Magento/ConfigurableProduct/_files/quote_with_configurable_product.php
48+
*/
49+
public function testUpdateConfigurableCartItemQuantity()
50+
{
51+
$reservedOrderId = 'test_cart_with_configurable';
52+
$maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute($reservedOrderId);
53+
54+
$productSku = 'simple_10';
55+
$newQuantity = 123;
56+
$quoteItem = $this->getQuoteItemBySku($productSku, $reservedOrderId);
57+
58+
$query = $this->getQuery($maskedQuoteId, (int)$quoteItem->getId(), $newQuantity);
59+
$response = $this->graphQlMutation($query);
60+
61+
self::assertArrayHasKey('updateCartItems', $response);
62+
self::assertArrayHasKey('quantity', $response['updateCartItems']['cart']['items']['0']);
63+
self::assertEquals($newQuantity, $response['updateCartItems']['cart']['items']['0']['quantity']);
64+
}
65+
66+
/**
67+
* @inheritdoc
68+
*/
69+
protected function setUp()
70+
{
71+
$objectManager = Bootstrap::getObjectManager();
72+
$this->getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class);
73+
$this->quoteFactory = $objectManager->get(QuoteFactory::class);
74+
$this->quoteResource = $objectManager->get(QuoteResource::class);
75+
$this->quoteIdMaskFactory = Bootstrap::getObjectManager()->get(QuoteIdMaskFactory::class);
76+
}
77+
78+
/**
79+
* @param string $maskedQuoteId
80+
* @param int $quoteItemId
81+
* @param int $newQuantity
82+
* @return string
83+
*/
84+
private function getQuery(string $maskedQuoteId, int $quoteItemId, int $newQuantity): string
85+
{
86+
return <<<QUERY
87+
mutation {
88+
updateCartItems(input: {
89+
cart_id:"$maskedQuoteId"
90+
cart_items: [
91+
{
92+
cart_item_id: $quoteItemId
93+
quantity: $newQuantity
94+
}
95+
]
96+
}) {
97+
cart {
98+
items {
99+
quantity
100+
}
101+
}
102+
}
103+
}
104+
QUERY;
105+
}
106+
107+
/**
108+
* Returns quote item by product SKU
109+
*
110+
* @param string $sku
111+
* @return Item|bool
112+
* @throws NoSuchEntityException
113+
*/
114+
private function getQuoteItemBySku(string $sku, string $reservedOrderId)
115+
{
116+
$quote = $this->quoteFactory->create();
117+
$this->quoteResource->load($quote, $reservedOrderId, 'reserved_order_id');
118+
$item = false;
119+
foreach ($quote->getAllItems() as $quoteItem) {
120+
if ($quoteItem->getSku() == $sku && $quoteItem->getProductType() == Configurable::TYPE_CODE &&
121+
!$quoteItem->getParentItemId()) {
122+
$item = $quoteItem;
123+
break;
124+
}
125+
}
126+
127+
return $item;
128+
}
129+
}

0 commit comments

Comments
 (0)