Skip to content

Commit 36971ef

Browse files
32615: [GraphQL] Add assign customer to cart mutation
1 parent d3f202a commit 36971ef

File tree

4 files changed

+392
-0
lines changed

4 files changed

+392
-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+
$message = $e->getMessage();
88+
if (str_contains($message, 'No such entity with cartId = ')) {
89+
$message = __('Could not find a cart with ID "%masked_cart_id"', ['masked_cart_id' => $guestMaskedCartId]);
90+
} else {
91+
$message = __($e->getMessage());
92+
}
93+
throw new GraphQlNoSuchEntityException($message, $e);
94+
} catch (StateException $e) {
95+
throw new GraphQlInputException(__($e->getMessage()), $e);
96+
} catch (LocalizedException $e) {
97+
throw new GraphQlInputException(__('Unable to assign the customer to the guest cart: %message', ['message' => $e->getMessage()]), $e);
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: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
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+
protected function setUp(): void
37+
{
38+
$objectManager = Bootstrap::getObjectManager();
39+
$this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class);
40+
$this->getQuoteByReservedOrderId = $objectManager->get(GetQuoteByReservedOrderId::class);
41+
$this->customerTokenService = $objectManager->get(CustomerTokenServiceInterface::class);
42+
}
43+
44+
/**
45+
* @magentoApiDataFixture Magento/Checkout/_files/quote_with_virtual_product_saved.php
46+
* @magentoApiDataFixture Magento/Customer/_files/customer.php
47+
* @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php
48+
* @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php
49+
* @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php
50+
*/
51+
public function testAssignCustomerToGuestCart()
52+
{
53+
$guestQuote = $this->getQuoteByReservedOrderId->execute('test_order_with_virtual_product_without_address');
54+
$guestQuoteItem = $guestQuote->getAllVisibleItems()[0];
55+
$guestQuoteMaskedId = $this->quoteIdToMaskedId->execute((int)$guestQuote->getId());
56+
57+
$customerQuote = $this->getQuoteByReservedOrderId->execute('test_quote');
58+
$customerQuoteItem = $customerQuote->getAllVisibleItems()[0];
59+
60+
$response = $this->graphQlMutation(
61+
$this->getAssignCustomerToGuestCartMutation($guestQuoteMaskedId),
62+
[],
63+
'',
64+
$this->getHeaderMap()
65+
);
66+
$this->assertArrayHasKey('assignCustomerToGuestCart', $response);
67+
$this->assertArrayHasKey('items', $response['assignCustomerToGuestCart']);
68+
$items = $response['assignCustomerToGuestCart']['items'];
69+
$this->assertCount(2,$items);
70+
71+
$this->assertEquals($customerQuoteItem->getQty(), $items[1]['quantity']);
72+
$this->assertEquals($customerQuoteItem->getSku(), $items[1]['product']['sku']);
73+
74+
$this->assertEquals($guestQuoteItem->getQty(), $items[0]['quantity']);
75+
$this->assertEquals($guestQuoteItem->getSku(), $items[0]['product']['sku']);
76+
}
77+
78+
/**
79+
* @magentoApiDataFixture Magento/Checkout/_files/quote_with_virtual_product_saved.php
80+
* @magentoApiDataFixture Magento/Customer/_files/customer.php
81+
* @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php
82+
* @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php
83+
* @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php
84+
*/
85+
public function testCustomerCartExpiryAfterAssigning()
86+
{
87+
$this->expectException(\Exception::class);
88+
$this->expectExceptionMessage('The cart isn\'t active.');
89+
90+
$guestQuote = $this->getQuoteByReservedOrderId->execute('test_order_with_virtual_product_without_address');
91+
$guestQuoteMaskedId = $this->quoteIdToMaskedId->execute((int)$guestQuote->getId());
92+
93+
$customerQuote = $this->getQuoteByReservedOrderId->execute('test_quote');
94+
$customerQuoteMaskedId = $this->quoteIdToMaskedId->execute((int)$customerQuote->getId());
95+
96+
$this->graphQlMutation(
97+
$this->getAssignCustomerToGuestCartMutation($guestQuoteMaskedId),
98+
[],
99+
'',
100+
$this->getHeaderMap()
101+
);
102+
$this->graphQlMutation(
103+
$this->getCartQuery($customerQuoteMaskedId),
104+
[],
105+
'',
106+
$this->getHeaderMap()
107+
);
108+
}
109+
110+
/**
111+
* @magentoApiDataFixture Magento/Customer/_files/customer.php
112+
*/
113+
public function testAssigningCustomerToNonExistentCart()
114+
{
115+
$guestQuoteMaskedId = "non_existent_masked_id";
116+
$this->expectException(\Exception::class);
117+
$this->expectExceptionMessage("Could not find a cart with ID \"{$guestQuoteMaskedId}\"");
118+
119+
$this->graphQlMutation(
120+
$this->getAssignCustomerToGuestCartMutation($guestQuoteMaskedId),
121+
[],
122+
'',
123+
$this->getHeaderMap()
124+
);
125+
}
126+
127+
/**
128+
* @magentoApiDataFixture Magento/Customer/_files/customer.php
129+
* @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php
130+
* @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php
131+
* @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php
132+
*/
133+
public function testAssignCustomerToCustomerCart()
134+
{
135+
$customerQuote = $this->getQuoteByReservedOrderId->execute('test_quote');
136+
$customerQuoteMaskedId = $this->quoteIdToMaskedId->execute((int)$customerQuote->getId());
137+
138+
$this->expectException(\Exception::class);
139+
$this->expectExceptionMessage("The current user cannot perform operations on cart \"{$customerQuoteMaskedId}\"");
140+
141+
$this->graphQlMutation(
142+
$this->getAssignCustomerToGuestCartMutation($customerQuoteMaskedId),
143+
[],
144+
'',
145+
$this->getHeaderMap()
146+
);
147+
}
148+
149+
/**
150+
* Create the assignCustomerToGuestCart mutation
151+
*
152+
* @param string $guestQuoteMaskedId
153+
* @return string
154+
*/
155+
private function getAssignCustomerToGuestCartMutation(string $guestQuoteMaskedId): string
156+
{
157+
return <<<QUERY
158+
mutation {
159+
assignCustomerToGuestCart(
160+
cart_id: "{$guestQuoteMaskedId}"
161+
){
162+
items {
163+
quantity
164+
product {
165+
sku
166+
}
167+
}
168+
}
169+
}
170+
QUERY;
171+
}
172+
173+
/**
174+
* Get cart query
175+
*
176+
* @param string $maskedId
177+
* @return string
178+
*/
179+
private function getCartQuery(string $maskedId): string
180+
{
181+
return <<<QUERY
182+
{
183+
cart(cart_id: "{$maskedId}") {
184+
items {
185+
quantity
186+
product {
187+
sku
188+
}
189+
}
190+
}
191+
}
192+
QUERY;
193+
}
194+
195+
/**
196+
* @param string $username
197+
* @param string $password
198+
* @return array
199+
* @throws \Magento\Framework\Exception\AuthenticationException
200+
*/
201+
private function getHeaderMap(string $username = 'customer@example.com', string $password = 'password'): array
202+
{
203+
$customerToken = $this->customerTokenService->createCustomerAccessToken($username, $password);
204+
$headerMap = ['Authorization' => 'Bearer ' . $customerToken];
205+
return $headerMap;
206+
}
207+
}

0 commit comments

Comments
 (0)