Skip to content

Commit 3927fbb

Browse files
committed
Merge remote-tracking branch 'act4/ACP2E-3774' into PR_Apr23_doleksandr
2 parents fe35614 + 3ee3987 commit 3927fbb

File tree

2 files changed

+184
-4
lines changed

2 files changed

+184
-4
lines changed

app/code/Magento/SalesGraphQl/Model/Resolver/Reorder.php

+28-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?php
22
/**
3-
* Copyright © Magento, Inc. All rights reserved.
4-
* See COPYING.txt for license details.
3+
* Copyright 2020 Adobe
4+
* All Rights Reserved.
55
*/
66
declare(strict_types=1);
77

@@ -11,6 +11,7 @@
1111
use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException;
1212
use Magento\Framework\GraphQl\Query\ResolverInterface;
1313
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
14+
use Magento\Framework\Lock\LockManagerInterface;
1415
use Magento\GraphQl\Model\Query\ContextInterface;
1516
use Magento\Sales\Model\Reorder\Data\Error;
1617
use Magento\Sales\Model\OrderFactory;
@@ -26,6 +27,10 @@ class Reorder implements ResolverInterface
2627
*/
2728
private const ARGUMENT_ORDER_NUMBER = 'orderNumber';
2829

30+
private const LOCK_PREFIX = 'reorder_lock_';
31+
32+
private const LOCK_TIMEOUT = 60;
33+
2934
/**
3035
* @var OrderFactory
3136
*/
@@ -36,16 +41,24 @@ class Reorder implements ResolverInterface
3641
*/
3742
private $reorder;
3843

44+
/**
45+
* @var LockManagerInterface
46+
*/
47+
private $lockManager;
48+
3949
/**
4050
* @param \Magento\Sales\Model\Reorder\Reorder $reorder
4151
* @param OrderFactory $orderFactory
52+
* @param LockManagerInterface $lockManager
4253
*/
4354
public function __construct(
4455
\Magento\Sales\Model\Reorder\Reorder $reorder,
45-
OrderFactory $orderFactory
56+
OrderFactory $orderFactory,
57+
LockManagerInterface $lockManager
4658
) {
4759
$this->orderFactory = $orderFactory;
4860
$this->reorder = $reorder;
61+
$this->lockManager = $lockManager;
4962
}
5063

5164
/**
@@ -74,7 +87,18 @@ public function resolve(
7487
);
7588
}
7689

77-
$reorderOutput = $this->reorder->execute($orderNumber, $storeId);
90+
$lockName = hash('sha256', $orderNumber);
91+
if ($this->lockManager->lock(self::LOCK_PREFIX . $lockName, self::LOCK_TIMEOUT)) {
92+
try {
93+
$reorderOutput = $this->reorder->execute($orderNumber, $storeId);
94+
} finally {
95+
$this->lockManager->unlock(self::LOCK_PREFIX . $lockName);
96+
}
97+
} else {
98+
throw new \Magento\Framework\Exception\LocalizedException(
99+
__('Sorry, there has been an error processing your request. Please try again later.')
100+
);
101+
}
78102

79103
return [
80104
'cart' => [
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
<?php
2+
/**
3+
* Copyright 2025 Adobe
4+
* All Rights Reserved.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\SalesGraphQl\Test\Unit\Model\Resolver;
9+
10+
use Magento\Framework\Exception\LocalizedException;
11+
use Magento\Framework\Lock\LockManagerInterface;
12+
use Magento\GraphQl\Model\Query\Context;
13+
use Magento\GraphQl\Model\Query\ContextExtensionInterface;
14+
use Magento\Sales\Model\Order;
15+
use Magento\Sales\Model\OrderFactory;
16+
use Magento\Sales\Model\Reorder\Reorder;
17+
use Magento\Store\Api\Data\StoreInterface;
18+
use PHPUnit\Framework\MockObject\MockObject;
19+
use PHPUnit\Framework\TestCase;
20+
use Magento\SalesGraphQl\Model\Resolver\Reorder as Subject;
21+
use Magento\Framework\GraphQl\Config\Element\Field;
22+
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
23+
24+
class ReorderTest extends TestCase
25+
{
26+
/**
27+
* @var Subject|MockObject
28+
*/
29+
private $subject;
30+
31+
/**
32+
* @var ContextExtensionInterface|MockObject
33+
*/
34+
private $extensionAttributesMock;
35+
36+
/**
37+
* @var Context|MockObject
38+
*/
39+
private $contextMock;
40+
41+
/**
42+
* @var OrderFactory|MockObject
43+
*/
44+
private $orderFactory;
45+
46+
/**
47+
* @var LockManagerInterface|MockObject
48+
*/
49+
private $lockManager;
50+
51+
/**
52+
* @var Reorder|MockObject
53+
*/
54+
private $reorder;
55+
56+
protected function setUp(): void
57+
{
58+
$this->reorder = $this->createMock(Reorder::class);
59+
$this->orderFactory = $this->createMock(OrderFactory::class);
60+
$this->lockManager = $this->createMock(LockManagerInterface::class);
61+
$this->contextMock = $this->createMock(Context::class);
62+
63+
$this->subject = new Subject(
64+
$this->reorder,
65+
$this->orderFactory,
66+
$this->lockManager
67+
);
68+
}
69+
70+
public function testResolve(): void
71+
{
72+
$fieldMock = $this->createMock(Field::class);
73+
$resolveInfoMock = $this->createMock(ResolveInfo::class);
74+
$args = ['orderNumber' => '00000010'];
75+
$value = [];
76+
77+
$this->prepareCommonFlow();
78+
79+
$this->lockManager->expects($this->once())
80+
->method('lock')
81+
->willReturn(true);
82+
$this->lockManager->expects($this->once())
83+
->method('unlock')
84+
->willReturn(true);
85+
86+
$result = $this->subject->resolve($fieldMock, $this->contextMock, $resolveInfoMock, $value, $args);
87+
88+
$this->assertIsArray($result);
89+
$this->assertArrayHasKey('cart', $result);
90+
$this->assertArrayHasKey('userInputErrors', $result);
91+
$this->assertEmpty($result['userInputErrors']);
92+
}
93+
94+
public function testResolveLockedAndThrowsError(): void
95+
{
96+
$fieldMock = $this->createMock(Field::class);
97+
$resolveInfoMock = $this->createMock(ResolveInfo::class);
98+
$args = ['orderNumber' => '00000010'];
99+
$value = [];
100+
101+
$this->prepareCommonFlow();
102+
103+
$this->lockManager->expects($this->once())
104+
->method('lock')
105+
->willReturn(false);
106+
$this->lockManager->expects($this->never())
107+
->method('unlock');
108+
109+
$exceptionMessage = 'Sorry, there has been an error processing your request. Please try again later.';
110+
$this->expectException(LocalizedException::class);
111+
$this->expectExceptionMessage($exceptionMessage);
112+
113+
$this->subject->resolve($fieldMock, $this->contextMock, $resolveInfoMock, $value, $args);
114+
}
115+
116+
private function prepareCommonFlow()
117+
{
118+
$contextCustomerId = 1;
119+
$orderCustomerId = 1;
120+
121+
$this->extensionAttributesMock = $this->getMockBuilder(ContextExtensionInterface::class)
122+
->disableOriginalConstructor()
123+
->addMethods(['getIsCustomer', 'getStore'])
124+
->getMockForAbstractClass();
125+
$this->extensionAttributesMock->expects($this->once())
126+
->method('getIsCustomer')
127+
->willReturn(true);
128+
129+
$store = $this->createMock(StoreInterface::class);
130+
$store->expects($this->once())
131+
->method('getId')
132+
->willReturn(1);
133+
$this->extensionAttributesMock->expects($this->once())
134+
->method('getStore')
135+
->willReturn($store);
136+
137+
$this->contextMock->expects($this->exactly(2))
138+
->method('getExtensionAttributes')
139+
->willReturn($this->extensionAttributesMock);
140+
141+
$this->contextMock->expects($this->once())
142+
->method('getUserId')
143+
->willReturn($contextCustomerId);
144+
145+
$order = $this->createMock(Order::class);
146+
$order->expects($this->once())
147+
->method('loadByIncrementIdAndStoreId')
148+
->willReturnSelf();
149+
$order->expects($this->once())
150+
->method('getCustomerId')
151+
->willReturn($orderCustomerId);
152+
$this->orderFactory->expects($this->once())
153+
->method('create')
154+
->willReturn($order);
155+
}
156+
}

0 commit comments

Comments
 (0)