Skip to content

Commit b370b01

Browse files
ENGCOM-9171: #32615: [GraphQL] Add assign customer to cart mutation #33106
- Merge Pull Request #33106 from khrystynastolbova/magento2:32615_assignCustomerToGuestCart - Merged commits: 1. 36971ef 2. 9ced6e0 3. 6daaf31
2 parents 6df210e + 6daaf31 commit b370b01

File tree

4 files changed

+411
-0
lines changed

4 files changed

+411
-0
lines changed
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\QuoteGraphQl\Model\Resolver;
9+
10+
use Magento\Framework\Exception\LocalizedException;
11+
use Magento\Framework\Exception\NoSuchEntityException;
12+
use Magento\Framework\Exception\StateException;
13+
use Magento\Framework\GraphQl\Config\Element\Field;
14+
use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException;
15+
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
16+
use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException;
17+
use Magento\Framework\GraphQl\Query\ResolverInterface;
18+
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
19+
use Magento\GraphQl\Model\Query\ContextInterface;
20+
use Magento\Quote\Api\GuestCartManagementInterface;
21+
use Magento\Quote\Model\Cart\CustomerCartResolver;
22+
use Magento\QuoteGraphQl\Model\Cart\GetCartForUser;
23+
24+
/**
25+
* Assign the customer to the guest cart resolver
26+
*
27+
* @SuppressWarnings(PHPMD.LongVariable)
28+
*/
29+
class AssignCustomerToGuestCart implements ResolverInterface
30+
{
31+
/**
32+
* @var GetCartForUser
33+
*/
34+
private $getCartForUser;
35+
36+
/**
37+
* @var CustomerCartResolver
38+
*/
39+
private $customerCartResolver;
40+
41+
/**
42+
* @var GuestCartManagementInterface
43+
*/
44+
private $guestCartManagementInterface;
45+
46+
/**
47+
* @param GetCartForUser $getCartForUser
48+
* @param GuestCartManagementInterface $guestCartManagementInterface
49+
* @param CustomerCartResolver $customerCartResolver
50+
*/
51+
public function __construct(
52+
GetCartForUser $getCartForUser,
53+
GuestCartManagementInterface $guestCartManagementInterface,
54+
CustomerCartResolver $customerCartResolver
55+
) {
56+
$this->getCartForUser = $getCartForUser;
57+
$this->guestCartManagementInterface = $guestCartManagementInterface;
58+
$this->customerCartResolver = $customerCartResolver;
59+
}
60+
61+
/**
62+
* @inheritdoc
63+
*/
64+
public function resolve(
65+
Field $field,
66+
$context,
67+
ResolveInfo $info,
68+
array $value = null,
69+
array $args = null
70+
) {
71+
/** @var ContextInterface $context */
72+
if (false === $context->getExtensionAttributes()->getIsCustomer()) {
73+
throw new GraphQlAuthorizationException(__(
74+
'The current customer isn\'t authorized.'
75+
));
76+
}
77+
78+
$currentUserId = $context->getUserId();
79+
$guestMaskedCartId = $args['cart_id'];
80+
$storeId = (int)$context->getExtensionAttributes()->getStore()->getId();
81+
82+
$this->getCartForUser->execute($guestMaskedCartId, null, $storeId);
83+
84+
try {
85+
$this->guestCartManagementInterface->assignCustomer($guestMaskedCartId, $currentUserId, $storeId);
86+
} catch (NoSuchEntityException $e) {
87+
throw new GraphQlNoSuchEntityException(
88+
__('Could not find a cart with ID "%masked_cart_id"', ['masked_cart_id' => $guestMaskedCartId]),
89+
$e
90+
);
91+
} catch (StateException $e) {
92+
throw new GraphQlInputException(__($e->getMessage()), $e);
93+
} catch (LocalizedException $e) {
94+
throw new GraphQlInputException(
95+
__('Unable to assign the customer to the guest cart: %message', ['message' => $e->getMessage()]),
96+
$e
97+
);
98+
}
99+
100+
try {
101+
$customerCart = $this->customerCartResolver->resolve($currentUserId);
102+
} catch (\Exception $e) {
103+
$customerCart = null;
104+
}
105+
return [
106+
'model' => $customerCart,
107+
];
108+
}
109+
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ type Mutation {
2020
setPaymentMethodOnCart(input: SetPaymentMethodOnCartInput): SetPaymentMethodOnCartOutput @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\SetPaymentMethodOnCart")
2121
setGuestEmailOnCart(input: SetGuestEmailOnCartInput): SetGuestEmailOnCartOutput @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\SetGuestEmailOnCart")
2222
setPaymentMethodAndPlaceOrder(input: SetPaymentMethodAndPlaceOrderInput): PlaceOrderOutput @deprecated(reason: "Should use setPaymentMethodOnCart and placeOrder mutations in single request.") @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\SetPaymentAndPlaceOrder")
23+
assignCustomerToGuestCart(cart_id: String!): Cart! @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\AssignCustomerToGuestCart") @doc(description:"Assign a logged in customer to the specified guest shopping cart.")
2324
mergeCarts(source_cart_id: String!, destination_cart_id: String): Cart! @doc(description:"Merges the source cart into the destination cart") @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\MergeCarts")
2425
placeOrder(input: PlaceOrderInput): PlaceOrderOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\PlaceOrder")
2526
addProductsToCart(cartId: String!, cartItems: [CartItemInput!]!): AddProductsToCartOutput @doc(description:"Add any type of product to the cart") @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\AddProductsToCart")
Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\GraphQl\Quote\Customer;
9+
10+
use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface;
11+
use Magento\TestFramework\Helper\Bootstrap;
12+
use Magento\TestFramework\Quote\Model\GetQuoteByReservedOrderId;
13+
use Magento\TestFramework\TestCase\GraphQlAbstract;
14+
use Magento\Integration\Api\CustomerTokenServiceInterface;
15+
16+
/**
17+
* Test for assigning customer to the guest cart
18+
*/
19+
class AssignCustomerToGuestCartTest extends GraphQlAbstract
20+
{
21+
/**
22+
* @var QuoteIdToMaskedQuoteIdInterface
23+
*/
24+
private $quoteIdToMaskedId;
25+
26+
/**
27+
* @var GetQuoteByReservedOrderId
28+
*/
29+
private $getQuoteByReservedOrderId;
30+
31+
/**
32+
* @var CustomerTokenServiceInterface
33+
*/
34+
private $customerTokenService;
35+
36+
/**
37+
* @inheritdoc
38+
*/
39+
protected function setUp(): void
40+
{
41+
$objectManager = Bootstrap::getObjectManager();
42+
$this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class);
43+
$this->getQuoteByReservedOrderId = $objectManager->get(GetQuoteByReservedOrderId::class);
44+
$this->customerTokenService = $objectManager->get(CustomerTokenServiceInterface::class);
45+
}
46+
47+
/**
48+
* Test for assigning customer to the guest cart
49+
*
50+
* @magentoApiDataFixture Magento/Checkout/_files/quote_with_virtual_product_saved.php
51+
* @magentoApiDataFixture Magento/Customer/_files/customer.php
52+
* @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php
53+
* @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php
54+
* @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php
55+
*/
56+
public function testAssignCustomerToGuestCart(): void
57+
{
58+
$guestQuote = $this->getQuoteByReservedOrderId->execute('test_order_with_virtual_product_without_address');
59+
$guestQuoteItem = $guestQuote->getAllVisibleItems()[0];
60+
$guestQuoteMaskedId = $this->quoteIdToMaskedId->execute((int)$guestQuote->getId());
61+
62+
$customerQuote = $this->getQuoteByReservedOrderId->execute('test_quote');
63+
$customerQuoteItem = $customerQuote->getAllVisibleItems()[0];
64+
65+
$response = $this->graphQlMutation(
66+
$this->getAssignCustomerToGuestCartMutation($guestQuoteMaskedId),
67+
[],
68+
'',
69+
$this->getHeaderMap()
70+
);
71+
$this->assertArrayHasKey('assignCustomerToGuestCart', $response);
72+
$this->assertArrayHasKey('items', $response['assignCustomerToGuestCart']);
73+
$items = $response['assignCustomerToGuestCart']['items'];
74+
$this->assertCount(2,$items);
75+
76+
$this->assertEquals($customerQuoteItem->getQty(), $items[1]['quantity']);
77+
$this->assertEquals($customerQuoteItem->getSku(), $items[1]['product']['sku']);
78+
79+
$this->assertEquals($guestQuoteItem->getQty(), $items[0]['quantity']);
80+
$this->assertEquals($guestQuoteItem->getSku(), $items[0]['product']['sku']);
81+
}
82+
83+
/**
84+
* Test that customer cart is expired after assigning
85+
*
86+
* @magentoApiDataFixture Magento/Checkout/_files/quote_with_virtual_product_saved.php
87+
* @magentoApiDataFixture Magento/Customer/_files/customer.php
88+
* @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php
89+
* @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php
90+
* @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php
91+
*/
92+
public function testCustomerCartExpiryAfterAssigning(): void
93+
{
94+
$this->expectException(\Exception::class);
95+
$this->expectExceptionMessage('The cart isn\'t active.');
96+
97+
$guestQuote = $this->getQuoteByReservedOrderId->execute('test_order_with_virtual_product_without_address');
98+
$guestQuoteMaskedId = $this->quoteIdToMaskedId->execute((int)$guestQuote->getId());
99+
100+
$customerQuote = $this->getQuoteByReservedOrderId->execute('test_quote');
101+
$customerQuoteMaskedId = $this->quoteIdToMaskedId->execute((int)$customerQuote->getId());
102+
103+
$this->graphQlMutation(
104+
$this->getAssignCustomerToGuestCartMutation($guestQuoteMaskedId),
105+
[],
106+
'',
107+
$this->getHeaderMap()
108+
);
109+
$this->graphQlMutation(
110+
$this->getCartQuery($customerQuoteMaskedId),
111+
[],
112+
'',
113+
$this->getHeaderMap()
114+
);
115+
}
116+
117+
/**
118+
* Test for assigning customer to non existent cart
119+
*
120+
* @magentoApiDataFixture Magento/Customer/_files/customer.php
121+
*/
122+
public function testAssigningCustomerToNonExistentCart(): void
123+
{
124+
$guestQuoteMaskedId = "non_existent_masked_id";
125+
$this->expectException(\Exception::class);
126+
$this->expectExceptionMessage("Could not find a cart with ID \"{$guestQuoteMaskedId}\"");
127+
128+
$this->graphQlMutation(
129+
$this->getAssignCustomerToGuestCartMutation($guestQuoteMaskedId),
130+
[],
131+
'',
132+
$this->getHeaderMap()
133+
);
134+
}
135+
136+
/**
137+
* Test for assigning customer to the customer cart
138+
*
139+
* @magentoApiDataFixture Magento/Customer/_files/customer.php
140+
* @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php
141+
* @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php
142+
* @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php
143+
*/
144+
public function testAssignCustomerToCustomerCart(): void
145+
{
146+
$customerQuote = $this->getQuoteByReservedOrderId->execute('test_quote');
147+
$customerQuoteMaskedId = $this->quoteIdToMaskedId->execute((int)$customerQuote->getId());
148+
149+
$this->expectException(\Exception::class);
150+
$this->expectExceptionMessage("The current user cannot perform operations on cart \"{$customerQuoteMaskedId}\"");
151+
152+
$this->graphQlMutation(
153+
$this->getAssignCustomerToGuestCartMutation($customerQuoteMaskedId),
154+
[],
155+
'',
156+
$this->getHeaderMap()
157+
);
158+
}
159+
160+
/**
161+
* Create the assignCustomerToGuestCart mutation
162+
*
163+
* @param string $guestQuoteMaskedId
164+
* @return string
165+
*/
166+
private function getAssignCustomerToGuestCartMutation(string $guestQuoteMaskedId): string
167+
{
168+
return <<<QUERY
169+
mutation {
170+
assignCustomerToGuestCart(
171+
cart_id: "{$guestQuoteMaskedId}"
172+
){
173+
items {
174+
quantity
175+
product {
176+
sku
177+
}
178+
}
179+
}
180+
}
181+
QUERY;
182+
}
183+
184+
/**
185+
* Get cart query
186+
*
187+
* @param string $maskedId
188+
* @return string
189+
*/
190+
private function getCartQuery(string $maskedId): string
191+
{
192+
return <<<QUERY
193+
{
194+
cart(cart_id: "{$maskedId}") {
195+
items {
196+
quantity
197+
product {
198+
sku
199+
}
200+
}
201+
}
202+
}
203+
QUERY;
204+
}
205+
206+
/**
207+
* Retrieve customer authorization headers
208+
*
209+
* @param string $username
210+
* @param string $password
211+
* @return array
212+
* @throws \Magento\Framework\Exception\AuthenticationException
213+
*/
214+
private function getHeaderMap(string $username = 'customer@example.com', string $password = 'password'): array
215+
{
216+
$customerToken = $this->customerTokenService->createCustomerAccessToken($username, $password);
217+
$headerMap = ['Authorization' => 'Bearer ' . $customerToken];
218+
return $headerMap;
219+
}
220+
}

0 commit comments

Comments
 (0)