Skip to content

Commit e65d0a5

Browse files
committed
Merge remote-tracking branch 'origin/2.3-develop' into MC-11925
2 parents 1a5cb90 + 1d1c2ea commit e65d0a5

File tree

39 files changed

+3049
-823
lines changed

39 files changed

+3049
-823
lines changed

app/code/Magento/Config/Model/Config/Backend/Serialized.php

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
use Magento\Framework\Serialize\Serializer\Json;
1010

1111
/**
12+
* Serialized backend model
13+
*
1214
* @api
1315
* @since 100.0.2
1416
*/
@@ -46,17 +48,32 @@ public function __construct(
4648
}
4749

4850
/**
51+
* Processing object after load data
52+
*
4953
* @return void
5054
*/
5155
protected function _afterLoad()
5256
{
5357
$value = $this->getValue();
5458
if (!is_array($value)) {
55-
$this->setValue(empty($value) ? false : $this->serializer->unserialize($value));
59+
try {
60+
$this->setValue(empty($value) ? false : $this->serializer->unserialize($value));
61+
} catch (\Exception $e) {
62+
$this->_logger->critical(
63+
sprintf(
64+
'Failed to unserialize %s config value. The error is: %s',
65+
$this->getPath(),
66+
$e->getMessage()
67+
)
68+
);
69+
$this->setValue(false);
70+
}
5671
}
5772
}
5873

5974
/**
75+
* Processing object before save data
76+
*
6077
* @return $this
6178
*/
6279
public function beforeSave()

app/code/Magento/Config/Test/Unit/Model/Config/Backend/SerializedTest.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@
99
use Magento\Framework\Model\Context;
1010
use Magento\Framework\Serialize\Serializer\Json;
1111
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
12+
use Psr\Log\LoggerInterface;
1213

14+
/**
15+
* Class SerializedTest
16+
*/
1317
class SerializedTest extends \PHPUnit\Framework\TestCase
1418
{
1519
/** @var \Magento\Config\Model\Config\Backend\Serialized */
@@ -18,14 +22,20 @@ class SerializedTest extends \PHPUnit\Framework\TestCase
1822
/** @var Json|\PHPUnit_Framework_MockObject_MockObject */
1923
private $serializerMock;
2024

25+
/** @var LoggerInterface|\PHPUnit_Framework_MockObject_MockObject */
26+
private $loggerMock;
27+
2128
protected function setUp()
2229
{
2330
$objectManager = new ObjectManager($this);
2431
$this->serializerMock = $this->createMock(Json::class);
32+
$this->loggerMock = $this->createMock(LoggerInterface::class);
2533
$contextMock = $this->createMock(Context::class);
2634
$eventManagerMock = $this->createMock(\Magento\Framework\Event\ManagerInterface::class);
2735
$contextMock->method('getEventDispatcher')
2836
->willReturn($eventManagerMock);
37+
$contextMock->method('getLogger')
38+
->willReturn($this->loggerMock);
2939
$this->serializedConfig = $objectManager->getObject(
3040
Serialized::class,
3141
[
@@ -72,6 +82,20 @@ public function afterLoadDataProvider()
7282
];
7383
}
7484

85+
public function testAfterLoadWithException()
86+
{
87+
$value = '{"key":';
88+
$expected = false;
89+
$this->serializedConfig->setValue($value);
90+
$this->serializerMock->expects($this->once())
91+
->method('unserialize')
92+
->willThrowException(new \Exception());
93+
$this->loggerMock->expects($this->once())
94+
->method('critical');
95+
$this->serializedConfig->afterLoad();
96+
$this->assertEquals($expected, $this->serializedConfig->getValue());
97+
}
98+
7599
/**
76100
* @param string $expected
77101
* @param int|double|string|array|boolean|null $value

app/code/Magento/Quote/Model/QuoteIdMask.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,14 @@ protected function _construct()
5353
* Initialize quote identifier before save
5454
*
5555
* @return $this
56+
* @throws \Magento\Framework\Exception\LocalizedException
5657
*/
5758
public function beforeSave()
5859
{
5960
parent::beforeSave();
60-
$this->setMaskedId($this->randomDataGenerator->getUniqueHash());
61+
if (empty($this->getMaskedId())) {
62+
$this->setMaskedId($this->randomDataGenerator->getUniqueHash());
63+
}
6164
return $this;
6265
}
6366
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
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\Cart;
9+
10+
use Magento\Quote\Api\CartManagementInterface;
11+
use Magento\Quote\Model\QuoteIdMaskFactory;
12+
use Magento\Quote\Model\ResourceModel\Quote\QuoteIdMask as QuoteIdMaskResourceModel;
13+
14+
/**
15+
* Create empty cart for customer
16+
*/
17+
class CreateEmptyCartForCustomer
18+
{
19+
/**
20+
* @var CartManagementInterface
21+
*/
22+
private $cartManagement;
23+
24+
/**
25+
* @var QuoteIdMaskFactory
26+
*/
27+
private $quoteIdMaskFactory;
28+
29+
/**
30+
* @var QuoteIdMaskResourceModel
31+
*/
32+
private $quoteIdMaskResourceModel;
33+
34+
/**
35+
* @param CartManagementInterface $cartManagement
36+
* @param QuoteIdMaskFactory $quoteIdMaskFactory
37+
* @param QuoteIdMaskResourceModel $quoteIdMaskResourceModel
38+
*/
39+
public function __construct(
40+
CartManagementInterface $cartManagement,
41+
QuoteIdMaskFactory $quoteIdMaskFactory,
42+
QuoteIdMaskResourceModel $quoteIdMaskResourceModel
43+
) {
44+
$this->cartManagement = $cartManagement;
45+
$this->quoteIdMaskFactory = $quoteIdMaskFactory;
46+
$this->quoteIdMaskResourceModel = $quoteIdMaskResourceModel;
47+
}
48+
49+
/**
50+
* Create empty cart for customer
51+
*
52+
* @param int $customerId
53+
* @param string|null $predefinedMaskedQuoteId
54+
* @return string
55+
*/
56+
public function execute(int $customerId, string $predefinedMaskedQuoteId = null): string
57+
{
58+
$quoteId = $this->cartManagement->createEmptyCartForCustomer($customerId);
59+
60+
$quoteIdMask = $this->quoteIdMaskFactory->create();
61+
$quoteIdMask->setQuoteId($quoteId);
62+
63+
if (isset($predefinedMaskedQuoteId)) {
64+
$quoteIdMask->setMaskedId($predefinedMaskedQuoteId);
65+
}
66+
67+
$this->quoteIdMaskResourceModel->save($quoteIdMask);
68+
return $quoteIdMask->getMaskedId();
69+
}
70+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
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\Cart;
9+
10+
use Magento\Quote\Api\GuestCartManagementInterface;
11+
use Magento\Quote\Model\QuoteIdMaskFactory;
12+
use Magento\Quote\Model\ResourceModel\Quote\QuoteIdMask as QuoteIdMaskResourceModel;
13+
14+
/**
15+
* Create empty cart for guest
16+
*/
17+
class CreateEmptyCartForGuest
18+
{
19+
/**
20+
* @var GuestCartManagementInterface
21+
*/
22+
private $guestCartManagement;
23+
24+
/**
25+
* @var QuoteIdMaskFactory
26+
*/
27+
private $quoteIdMaskFactory;
28+
29+
/**
30+
* @var QuoteIdMaskResourceModel
31+
*/
32+
private $quoteIdMaskResourceModel;
33+
34+
/**
35+
* @param GuestCartManagementInterface $guestCartManagement
36+
* @param QuoteIdMaskFactory $quoteIdMaskFactory
37+
* @param QuoteIdMaskResourceModel $quoteIdMaskResourceModel
38+
*/
39+
public function __construct(
40+
GuestCartManagementInterface $guestCartManagement,
41+
QuoteIdMaskFactory $quoteIdMaskFactory,
42+
QuoteIdMaskResourceModel $quoteIdMaskResourceModel
43+
) {
44+
$this->guestCartManagement = $guestCartManagement;
45+
$this->quoteIdMaskFactory = $quoteIdMaskFactory;
46+
$this->quoteIdMaskResourceModel = $quoteIdMaskResourceModel;
47+
}
48+
49+
/**
50+
* Create empty cart for guest
51+
*
52+
* @param string|null $predefinedMaskedQuoteId
53+
* @return string
54+
*/
55+
public function execute(string $predefinedMaskedQuoteId = null): string
56+
{
57+
$maskedQuoteId = $this->guestCartManagement->createEmptyCart();
58+
59+
if (isset($predefinedMaskedQuoteId)) {
60+
$quoteIdMask = $this->quoteIdMaskFactory->create();
61+
$this->quoteIdMaskResourceModel->load($quoteIdMask, $maskedQuoteId, 'masked_id');
62+
63+
$quoteIdMask->setMaskedId($predefinedMaskedQuoteId);
64+
$this->quoteIdMaskResourceModel->save($quoteIdMask);
65+
}
66+
return $predefinedMaskedQuoteId ?? $maskedQuoteId;
67+
}
68+
}

app/code/Magento/QuoteGraphQl/Model/Resolver/CreateEmptyCart.php

Lines changed: 62 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -7,55 +7,49 @@
77

88
namespace Magento\QuoteGraphQl\Model\Resolver;
99

10+
use Magento\Framework\Exception\NoSuchEntityException;
1011
use Magento\Framework\GraphQl\Config\Element\Field;
12+
use Magento\Framework\GraphQl\Exception\GraphQlAlreadyExistsException;
13+
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
1114
use Magento\Framework\GraphQl\Query\ResolverInterface;
1215
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
13-
use Magento\Quote\Api\CartManagementInterface;
14-
use Magento\Quote\Api\GuestCartManagementInterface;
15-
use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface;
16-
use Magento\Quote\Model\QuoteIdMaskFactory;
16+
use Magento\Quote\Model\MaskedQuoteIdToQuoteIdInterface;
17+
use Magento\QuoteGraphQl\Model\Cart\CreateEmptyCartForCustomer;
18+
use Magento\QuoteGraphQl\Model\Cart\CreateEmptyCartForGuest;
1719

1820
/**
1921
* @inheritdoc
2022
*/
2123
class CreateEmptyCart implements ResolverInterface
2224
{
2325
/**
24-
* @var CartManagementInterface
26+
* @var CreateEmptyCartForCustomer
2527
*/
26-
private $cartManagement;
28+
private $createEmptyCartForCustomer;
2729

2830
/**
29-
* @var GuestCartManagementInterface
31+
* @var CreateEmptyCartForGuest
3032
*/
31-
private $guestCartManagement;
33+
private $createEmptyCartForGuest;
3234

3335
/**
34-
* @var QuoteIdToMaskedQuoteIdInterface
36+
* @var MaskedQuoteIdToQuoteIdInterface
3537
*/
36-
private $quoteIdToMaskedId;
38+
private $maskedQuoteIdToQuoteId;
3739

3840
/**
39-
* @var QuoteIdMaskFactory
40-
*/
41-
private $quoteIdMaskFactory;
42-
43-
/**
44-
* @param CartManagementInterface $cartManagement
45-
* @param GuestCartManagementInterface $guestCartManagement
46-
* @param QuoteIdToMaskedQuoteIdInterface $quoteIdToMaskedId
47-
* @param QuoteIdMaskFactory $quoteIdMaskFactory
41+
* @param CreateEmptyCartForCustomer $createEmptyCartForCustomer
42+
* @param CreateEmptyCartForGuest $createEmptyCartForGuest
43+
* @param MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId
4844
*/
4945
public function __construct(
50-
CartManagementInterface $cartManagement,
51-
GuestCartManagementInterface $guestCartManagement,
52-
QuoteIdToMaskedQuoteIdInterface $quoteIdToMaskedId,
53-
QuoteIdMaskFactory $quoteIdMaskFactory
46+
CreateEmptyCartForCustomer $createEmptyCartForCustomer,
47+
CreateEmptyCartForGuest $createEmptyCartForGuest,
48+
MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId
5449
) {
55-
$this->cartManagement = $cartManagement;
56-
$this->guestCartManagement = $guestCartManagement;
57-
$this->quoteIdToMaskedId = $quoteIdToMaskedId;
58-
$this->quoteIdMaskFactory = $quoteIdMaskFactory;
50+
$this->createEmptyCartForCustomer = $createEmptyCartForCustomer;
51+
$this->createEmptyCartForGuest = $createEmptyCartForGuest;
52+
$this->maskedQuoteIdToQuoteId = $maskedQuoteIdToQuoteId;
5953
}
6054

6155
/**
@@ -65,19 +59,49 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value
6559
{
6660
$customerId = $context->getUserId();
6761

68-
if (0 !== $customerId && null !== $customerId) {
69-
$quoteId = $this->cartManagement->createEmptyCartForCustomer($customerId);
70-
$maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$quoteId);
71-
72-
if (empty($maskedQuoteId)) {
73-
$quoteIdMask = $this->quoteIdMaskFactory->create();
74-
$quoteIdMask->setQuoteId($quoteId)->save();
75-
$maskedQuoteId = $quoteIdMask->getMaskedId();
76-
}
77-
} else {
78-
$maskedQuoteId = $this->guestCartManagement->createEmptyCart();
62+
$predefinedMaskedQuoteId = null;
63+
if (isset($args['input']['cart_id'])) {
64+
$predefinedMaskedQuoteId = $args['input']['cart_id'];
65+
$this->validateMaskedId($predefinedMaskedQuoteId);
7966
}
8067

68+
$maskedQuoteId = (0 === $customerId || null === $customerId)
69+
? $this->createEmptyCartForGuest->execute($predefinedMaskedQuoteId)
70+
: $this->createEmptyCartForCustomer->execute($customerId, $predefinedMaskedQuoteId);
8171
return $maskedQuoteId;
8272
}
73+
74+
/**
75+
* Validate masked id
76+
*
77+
* @param string $maskedId
78+
* @throws GraphQlAlreadyExistsException
79+
* @throws GraphQlInputException
80+
*/
81+
private function validateMaskedId(string $maskedId): void
82+
{
83+
if (mb_strlen($maskedId) != 32) {
84+
throw new GraphQlInputException(__('Cart ID length should to be 32 symbols.'));
85+
}
86+
87+
if ($this->isQuoteWithSuchMaskedIdAlreadyExists($maskedId)) {
88+
throw new GraphQlAlreadyExistsException(__('Cart with ID "%1" already exists.', $maskedId));
89+
}
90+
}
91+
92+
/**
93+
* Check is quote with such maskedId already exists
94+
*
95+
* @param string $maskedId
96+
* @return bool
97+
*/
98+
private function isQuoteWithSuchMaskedIdAlreadyExists(string $maskedId): bool
99+
{
100+
try {
101+
$this->maskedQuoteIdToQuoteId->execute($maskedId);
102+
return true;
103+
} catch (NoSuchEntityException $e) {
104+
return false;
105+
}
106+
}
83107
}

0 commit comments

Comments
 (0)