Skip to content

Commit fda4e8c

Browse files
committed
LYNX-259: Created new mutation with test coverage and deprecated mutation
1 parent bc1c7ba commit fda4e8c

File tree

4 files changed

+433
-1
lines changed

4 files changed

+433
-1
lines changed
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
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\NoSuchEntityException;
11+
use Magento\Framework\GraphQl\Config\Element\Field;
12+
use Magento\Framework\GraphQl\Exception\GraphQlAlreadyExistsException;
13+
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
14+
use Magento\Framework\GraphQl\Query\ResolverInterface;
15+
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
16+
use Magento\Quote\Api\CartRepositoryInterface;
17+
use Magento\Quote\Model\MaskedQuoteIdToQuoteIdInterface;
18+
use Magento\QuoteGraphQl\Model\Cart\CreateEmptyCartForGuest;
19+
20+
/**
21+
* Creates a guest cart
22+
*/
23+
class CreateGuestCart implements ResolverInterface
24+
{
25+
/**
26+
* @var CreateEmptyCartForGuest
27+
*/
28+
private CreateEmptyCartForGuest $createEmptyCartForGuest;
29+
30+
/**
31+
* @var MaskedQuoteIdToQuoteIdInterface
32+
*/
33+
private MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId;
34+
35+
/**
36+
* @var CartRepositoryInterface
37+
*/
38+
private CartRepositoryInterface $cartRepository;
39+
40+
/**
41+
* @param CreateEmptyCartForGuest $createEmptyCartForGuest
42+
* @param MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId
43+
* @param CartRepositoryInterface $cartRepository
44+
*/
45+
public function __construct(
46+
CreateEmptyCartForGuest $createEmptyCartForGuest,
47+
MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId,
48+
CartRepositoryInterface $cartRepository
49+
) {
50+
$this->createEmptyCartForGuest = $createEmptyCartForGuest;
51+
$this->maskedQuoteIdToQuoteId = $maskedQuoteIdToQuoteId;
52+
$this->cartRepository = $cartRepository;
53+
}
54+
55+
/**
56+
* Creates an empty cart for guest and returns a cart object
57+
*
58+
* @param Field $field
59+
* @param $context
60+
* @param ResolveInfo $info
61+
* @param array|null $value
62+
* @param array|null $args
63+
* @return array[]
64+
* @throws GraphQlAlreadyExistsException
65+
* @throws GraphQlInputException
66+
* @throws NoSuchEntityException
67+
*/
68+
public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null)
69+
{
70+
$customerId = $context->getUserId();
71+
72+
$predefinedMaskedQuoteId = null;
73+
if (isset($args['input']['cart_uid'])) {
74+
$predefinedMaskedQuoteId = $args['input']['cart_uid'];
75+
$this->validateMaskedId($predefinedMaskedQuoteId);
76+
}
77+
78+
if (0 === $customerId || null === $customerId) {
79+
$maskedQuoteId = $this->createEmptyCartForGuest->execute($predefinedMaskedQuoteId);
80+
$cartId = $this->maskedQuoteIdToQuoteId->execute($maskedQuoteId);
81+
$cart = $this->cartRepository->get($cartId);
82+
} else {
83+
throw new GraphQlAlreadyExistsException(__('Use `Query.customerCart` for logged in customer.'));
84+
}
85+
86+
return [
87+
'cart' => [
88+
'model' => $cart,
89+
],
90+
];
91+
}
92+
93+
/**
94+
* Validate masked id
95+
*
96+
* @param string $maskedId
97+
* @throws GraphQlAlreadyExistsException
98+
* @throws GraphQlInputException
99+
*/
100+
private function validateMaskedId(string $maskedId): void
101+
{
102+
if (mb_strlen($maskedId) != 32) {
103+
throw new GraphQlInputException(__('Cart ID length should be 32 characters.'));
104+
}
105+
106+
if ($this->isQuoteWithSuchMaskedIdAlreadyExists($maskedId)) {
107+
throw new GraphQlAlreadyExistsException(__('Cart with ID "%1" already exists.', $maskedId));
108+
}
109+
}
110+
111+
/**
112+
* Check is quote with such maskedId already exists
113+
*
114+
* @param string $maskedId
115+
* @return bool
116+
*/
117+
private function isQuoteWithSuchMaskedIdAlreadyExists(string $maskedId): bool
118+
{
119+
try {
120+
$this->maskedQuoteIdToQuoteId->execute($maskedId);
121+
return true;
122+
} catch (NoSuchEntityException $e) {
123+
return false;
124+
}
125+
}
126+
}

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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ type Query {
88
}
99

1010
type Mutation {
11-
createEmptyCart(input: createEmptyCartInput @doc(description: "An optional input object that assigns the specified ID to the cart.")): String @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\CreateEmptyCart") @doc(description:"Create an empty shopping cart for a guest or logged in user")
11+
createEmptyCart(input: createEmptyCartInput): String @deprecated(reason: "Use `Mutation.createGuestCart`, or `Query.customerCart` for logged in customer")
12+
createGuestCart(input: CreateGuestCartInput): CreateGuestCartOutput @doc(description: "Create a new shopping cart") @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\CreateGuestCart")
1213
addSimpleProductsToCart(input: AddSimpleProductsToCartInput @doc(description: "An input object that defines which simple products to add to the cart.")): AddSimpleProductsToCartOutput @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\AddSimpleProductsToCart") @doc(description:"Add one or more simple products to the specified cart. We recommend using `addProductsToCart` instead.")
1314
addVirtualProductsToCart(input: AddVirtualProductsToCartInput @doc(description: "An input object that defines which virtual products to add to the cart.")): AddVirtualProductsToCartOutput @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\AddSimpleProductsToCart") @doc(description:"Add one or more virtual products to the specified cart. We recommend using `addProductsToCart` instead.")
1415
applyCouponToCart(input: ApplyCouponToCartInput @doc(description: "An input object that defines the coupon code to apply to the cart.")): ApplyCouponToCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\ApplyCouponToCart") @doc(description:"Apply a pre-defined coupon code to the specified cart.")
@@ -29,6 +30,14 @@ type Mutation {
2930
addProductsToCart(cartId: String! @doc(description: "The cart ID of the shopper."), cartItems: [CartItemInput!]! @doc(description: "An array that defines the products to add to the cart.")): AddProductsToCartOutput @doc(description:"Add any type of product to the cart.") @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\AddProductsToCart")
3031
}
3132

33+
input CreateGuestCartInput {
34+
cart_uid: ID @doc(description: "Optional client-generated ID")
35+
}
36+
37+
type CreateGuestCartOutput {
38+
cart: Cart
39+
}
40+
3241
input createEmptyCartInput @doc(description: "Assigns a specific `cart_id` to the empty cart.") {
3342
cart_id: String @doc(description: "The ID to assign to the cart.")
3443
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
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\Integration\Api\CustomerTokenServiceInterface;
11+
use Magento\Quote\Model\QuoteIdMaskFactory;
12+
use Magento\Quote\Model\ResourceModel\Quote\CollectionFactory as QuoteCollectionFactory;
13+
use Magento\Quote\Model\ResourceModel\Quote as QuoteResource;
14+
use Magento\TestFramework\Helper\Bootstrap;
15+
use Magento\TestFramework\TestCase\GraphQlAbstract;
16+
17+
/**
18+
* Test for guest cart creation mutation for customer
19+
*/
20+
class CreateGuestCartTest extends GraphQlAbstract
21+
{
22+
/**
23+
* @var CustomerTokenServiceInterface
24+
*/
25+
private $customerTokenService;
26+
27+
/**
28+
* @var QuoteCollectionFactory
29+
*/
30+
private $quoteCollectionFactory;
31+
32+
/**
33+
* @var QuoteResource
34+
*/
35+
private $quoteResource;
36+
37+
/**
38+
* @var QuoteIdMaskFactory
39+
*/
40+
private $quoteIdMaskFactory;
41+
42+
protected function setUp(): void
43+
{
44+
$objectManager = Bootstrap::getObjectManager();
45+
$this->customerTokenService = $objectManager->get(CustomerTokenServiceInterface::class);
46+
$this->quoteCollectionFactory = $objectManager->get(QuoteCollectionFactory::class);
47+
$this->quoteResource = $objectManager->get(QuoteResource::class);
48+
$this->quoteIdMaskFactory = $objectManager->get(QuoteIdMaskFactory::class);
49+
}
50+
51+
/**
52+
* @magentoApiDataFixture Magento/Customer/_files/customer.php
53+
*/
54+
public function testFailForLoggedInUser()
55+
{
56+
$this->expectException(\Exception::class);
57+
$this->expectExceptionMessage("Use `Query.customerCart` for logged in customer.");
58+
59+
$query = $this->getQuery();
60+
$this->graphQlMutation($query, [], '', $this->getHeaderMapWithCustomerToken());
61+
}
62+
63+
/**
64+
* @return string
65+
*/
66+
private function getQuery(): string
67+
{
68+
return <<<QUERY
69+
mutation {
70+
createGuestCart {
71+
cart {
72+
id
73+
}
74+
}
75+
}
76+
QUERY;
77+
}
78+
79+
/**
80+
* @param string $username
81+
* @param string $password
82+
* @return array
83+
*/
84+
private function getHeaderMapWithCustomerToken(
85+
string $username = '[email protected]',
86+
string $password = 'password'
87+
): array {
88+
$customerToken = $this->customerTokenService->createCustomerAccessToken($username, $password);
89+
$headerMap = ['Authorization' => 'Bearer ' . $customerToken];
90+
return $headerMap;
91+
}
92+
93+
protected function tearDown(): void
94+
{
95+
$quoteCollection = $this->quoteCollectionFactory->create();
96+
foreach ($quoteCollection as $quote) {
97+
$this->quoteResource->delete($quote);
98+
99+
$quoteIdMask = $this->quoteIdMaskFactory->create();
100+
$quoteIdMask->setQuoteId($quote->getId())
101+
->delete();
102+
}
103+
parent::tearDown();
104+
}
105+
}

0 commit comments

Comments
 (0)