Skip to content

Commit 3842a53

Browse files
committed
Merge remote-tracking branch 'origin/ACP2E-4031' into PR_2025_07_18_chittima
2 parents 7257f46 + 8c32ebb commit 3842a53

File tree

5 files changed

+100
-61
lines changed

5 files changed

+100
-61
lines changed
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
/**
3+
* Copyright 2025 Adobe
4+
* All Rights Reserved.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\QuoteGraphQl\Model;
9+
10+
use Magento\Framework\Exception\AuthorizationException;
11+
use Magento\Framework\Exception\LocalizedException;
12+
use Magento\Framework\GraphQl\Config\Element\Field;
13+
use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException;
14+
use Magento\Framework\GraphQl\Query\Resolver\ContextInterface;
15+
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
16+
use Magento\GraphQl\Helper\Error\AggregateExceptionMessageFormatter;
17+
18+
class OrderErrorProcessor
19+
{
20+
/**
21+
* @param AggregateExceptionMessageFormatter $errorMessageFormatter
22+
* @param ErrorMapper $errorMapper
23+
*/
24+
public function __construct(
25+
private readonly AggregateExceptionMessageFormatter $errorMessageFormatter,
26+
private readonly ErrorMapper $errorMapper
27+
) {
28+
}
29+
30+
/**
31+
* Process exception thrown by ordering process
32+
*
33+
* @param LocalizedException $exception
34+
* @param Field $field
35+
* @param ContextInterface $context
36+
* @param ResolveInfo $info
37+
* @throws GraphQlAuthorizationException
38+
* @throws QuoteException
39+
*/
40+
public function execute(
41+
LocalizedException $exception,
42+
Field $field,
43+
ContextInterface $context,
44+
ResolveInfo $info
45+
): void {
46+
$exception = $this->errorMessageFormatter->getFormatted(
47+
$exception,
48+
__('A server error stopped your order from being placed. ' .
49+
'Please try to place your order again'),
50+
'Unable to place order',
51+
$field,
52+
$context,
53+
$info
54+
);
55+
$exceptionCode = $exception->getCode();
56+
if (!$exceptionCode) {
57+
$exceptionCode = $this->errorMapper->getErrorMessageId($exception->getRawMessage());
58+
}
59+
60+
throw new QuoteException(__($exception->getMessage()), $exception, $exceptionCode);
61+
}
62+
}

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

Lines changed: 6 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,14 @@
1414
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
1515
use Magento\Framework\GraphQl\Query\ResolverInterface;
1616
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
17-
use Magento\GraphQl\Helper\Error\AggregateExceptionMessageFormatter;
1817
use Magento\QuoteGraphQl\Model\Cart\GetCartForCheckout;
1918
use Magento\QuoteGraphQl\Model\Cart\PlaceOrder as PlaceOrderModel;
20-
use Magento\QuoteGraphQl\Model\ErrorMapper;
21-
use Magento\QuoteGraphQl\Model\QuoteException;
19+
use Magento\QuoteGraphQl\Model\OrderErrorProcessor;
2220
use Magento\Sales\Api\OrderRepositoryInterface;
2321
use Magento\SalesGraphQl\Model\Formatter\Order as OrderFormatter;
2422

2523
/**
2624
* Resolver for placing order after payment method has already been set
27-
*
28-
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
2925
*/
3026
class PlaceOrder implements ResolverInterface
3127
{
@@ -34,16 +30,14 @@ class PlaceOrder implements ResolverInterface
3430
* @param PlaceOrderModel $placeOrder
3531
* @param OrderRepositoryInterface $orderRepository
3632
* @param OrderFormatter $orderFormatter
37-
* @param AggregateExceptionMessageFormatter $errorMessageFormatter
38-
* @param ErrorMapper $errorMapper
33+
* @param OrderErrorProcessor $orderErrorProcessor
3934
*/
4035
public function __construct(
4136
private readonly GetCartForCheckout $getCartForCheckout,
4237
private readonly PlaceOrderModel $placeOrder,
4338
private readonly OrderRepositoryInterface $orderRepository,
4439
private readonly OrderFormatter $orderFormatter,
45-
private readonly AggregateExceptionMessageFormatter $errorMessageFormatter,
46-
private readonly ErrorMapper $errorMapper
40+
private readonly OrderErrorProcessor $orderErrorProcessor
4741
) {
4842
}
4943

@@ -59,29 +53,15 @@ public function resolve(Field $field, $context, ResolveInfo $info, ?array $value
5953
$maskedCartId = $args['input']['cart_id'];
6054
$userId = (int)$context->getUserId();
6155
$storeId = (int)$context->getExtensionAttributes()->getStore()->getId();
56+
$order = null;
6257
try {
6358
$cart = $this->getCartForCheckout->execute($maskedCartId, $userId, $storeId);
6459
$orderId = $this->placeOrder->execute($cart, $maskedCartId, $userId);
6560
$order = $this->orderRepository->get($orderId);
6661
} catch (AuthorizationException $exception) {
67-
throw new GraphQlAuthorizationException(
68-
__($exception->getMessage())
69-
);
62+
throw new GraphQlAuthorizationException(__($exception->getMessage()));
7063
} catch (LocalizedException $exception) {
71-
$exception = $this->errorMessageFormatter->getFormatted(
72-
$exception,
73-
__('A server error stopped your order from being placed. Please try to place your order again'),
74-
'Unable to place order',
75-
$field,
76-
$context,
77-
$info
78-
);
79-
$exceptionCode = $exception->getCode();
80-
if (!$exceptionCode) {
81-
$exceptionCode = $this->errorMapper->getErrorMessageId($exception->getRawMessage());
82-
}
83-
84-
throw new QuoteException(__($exception->getMessage()), $exception, $exceptionCode);
64+
$this->orderErrorProcessor->execute($exception, $field, $context, $info);
8565
}
8666

8767
return [

app/code/Magento/QuoteGraphQl/Test/Unit/Model/Resolver/PlaceOrderTranslationTest.php renamed to app/code/Magento/QuoteGraphQl/Test/Unit/Model/Resolver/PlaceOrderExceptionProcessingTest.php

Lines changed: 17 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,12 @@
1010
use Magento\Framework\GraphQl\Config\Element\Field;
1111
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
1212
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
13-
use Magento\GraphQl\Helper\Error\AggregateExceptionMessageFormatter;
1413
use Magento\GraphQl\Model\Query\Context;
1514
use Magento\GraphQl\Model\Query\ContextExtensionInterface;
1615
use Magento\Quote\Model\Quote;
1716
use Magento\QuoteGraphQl\Model\Cart\GetCartForCheckout;
1817
use Magento\QuoteGraphQl\Model\Cart\PlaceOrder as PlaceOrderModel;
19-
use Magento\QuoteGraphQl\Model\ErrorMapper;
18+
use Magento\QuoteGraphQl\Model\OrderErrorProcessor;
2019
use Magento\QuoteGraphQl\Model\QuoteException;
2120
use Magento\QuoteGraphQl\Model\Resolver\PlaceOrder;
2221
use Magento\Sales\Api\OrderRepositoryInterface;
@@ -28,7 +27,7 @@
2827
/**
2928
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
3029
*/
31-
class PlaceOrderTranslationTest extends TestCase
30+
class PlaceOrderExceptionProcessingTest extends TestCase
3231
{
3332
/**
3433
* @var GetCartForCheckout|MockObject
@@ -41,14 +40,9 @@ class PlaceOrderTranslationTest extends TestCase
4140
private $placeOrderModelMock;
4241

4342
/**
44-
* @var AggregateExceptionMessageFormatter|MockObject
43+
* @var OrderErrorProcessor|MockObject
4544
*/
46-
private $errorMessageFormatterMock;
47-
48-
/**
49-
* @var ErrorMapper|MockObject
50-
*/
51-
private $errorMapperMock;
45+
private $orderErrorProcessor;
5246

5347
/**
5448
* @var PlaceOrder
@@ -59,42 +53,27 @@ protected function setUp(): void
5953
{
6054
$this->getCartForCheckoutMock = $this->createMock(GetCartForCheckout::class);
6155
$this->placeOrderModelMock = $this->createMock(PlaceOrderModel::class);
62-
$this->errorMessageFormatterMock = $this->createMock(AggregateExceptionMessageFormatter::class);
63-
$this->errorMapperMock = $this->createMock(ErrorMapper::class);
56+
$this->orderErrorProcessor = $this->createMock(OrderErrorProcessor::class);
6457

6558
$this->placeOrderResolver = new PlaceOrder(
6659
$this->getCartForCheckoutMock,
6760
$this->placeOrderModelMock,
6861
$this->createMock(OrderRepositoryInterface::class),
6962
$this->createMock(OrderFormatter::class),
70-
$this->errorMessageFormatterMock,
71-
$this->errorMapperMock
63+
$this->orderErrorProcessor
7264
);
7365
}
7466

7567
/**
76-
* Test that getRawMessage() is called on GraphQlInputException to map the error message properly.
68+
* Test that OrderErrorProcessor::execute method is being triggered on thrown LocalizedException
7769
*/
78-
public function testGetRawMessageIsCalledForErrorMapping(): void
70+
public function testExceptionProcessing(): void
7971
{
80-
$exception = $this->getMockBuilder(GraphQlInputException::class)
81-
->disableOriginalConstructor()
82-
->onlyMethods(['getRawMessage'])
83-
->getMock();
84-
$exception->method('getRawMessage')->willReturn('Raw error message');
85-
$exception->expects($this->once())->method('getRawMessage');
86-
87-
$this->errorMapperMock->expects($this->once())
88-
->method('getErrorMessageId')
89-
->with('Raw error message')
90-
->willReturn(1);
91-
72+
$exception = $this->createMock(GraphQlInputException::class);
9273
$this->getCartForCheckoutMock->method('execute')->willReturn($this->createMock(Quote::class));
9374
$this->placeOrderModelMock->method('execute')->willThrowException($exception);
94-
$this->errorMessageFormatterMock->method('getFormatted')->willReturn($exception);
9575

9676
$contextMock = $this->createMock(Context::class);
97-
9877
$extensionAttributesMock = $this->getMockBuilder(ContextExtensionInterface::class)
9978
->disableOriginalConstructor()
10079
->addMethods(
@@ -106,11 +85,18 @@ public function testGetRawMessageIsCalledForErrorMapping(): void
10685
$extensionAttributesMock->method('getStore')->willReturn($this->createMock(StoreInterface::class));
10786
$contextMock->method('getExtensionAttributes')->willReturn($extensionAttributesMock);
10887

88+
$field = $this->createMock(Field::class);
89+
$info = $this->createMock(ResolveInfo::class);
90+
$this->orderErrorProcessor->expects($this->once())
91+
->method('execute')
92+
->with($exception, $field, $contextMock)
93+
->willThrowException($this->createMock(QuoteException::class));
94+
10995
$this->expectException(QuoteException::class);
11096
$this->placeOrderResolver->resolve(
11197
$this->createMock(Field::class),
11298
$contextMock,
113-
$this->createMock(ResolveInfo::class),
99+
$info,
114100
null,
115101
['input' => ['cart_id' => 'masked_cart_id']]
116102
);

app/code/Magento/QuoteGraphQl/etc/graphql/di.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
</argument>
4545
</arguments>
4646
</virtualType>
47-
<type name="Magento\QuoteGraphQl\Model\Resolver\PlaceOrder">
47+
<type name="Magento\QuoteGraphQl\Model\OrderErrorProcessor">
4848
<arguments>
4949
<argument name="errorMessageFormatter" xsi:type="object">Magento\QuoteGraphQl\Helper\Error\PlaceOrderMessageFormatter</argument>
5050
</arguments>

lib/internal/Magento/Framework/GraphQl/Query/QueryProcessor.php

Lines changed: 14 additions & 3 deletions
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 2017 Adobe
4+
* All Rights Reserved.
55
*/
66
declare(strict_types=1);
77

@@ -88,7 +88,7 @@ public function process(
8888
}
8989

9090
$rootValue = null;
91-
return GraphQL::executeQuery(
91+
$executionResult = GraphQL::executeQuery(
9292
$schema,
9393
$source,
9494
$rootValue,
@@ -100,5 +100,16 @@ public function process(
100100
)->toArray(
101101
(int) ($this->exceptionFormatter->shouldShowDetail() ? DebugFlag::INCLUDE_DEBUG_MESSAGE : false)
102102
);
103+
if (!empty($executionResult['errors'])) {
104+
foreach ($executionResult['errors'] as $error) {
105+
if (isset($error['extensions']['error_code'])) {
106+
$executionResult['data']['errors'][] = [
107+
'message' => $error['message'],
108+
'code' => $error['extensions']['error_code']
109+
];
110+
}
111+
}
112+
}
113+
return $executionResult;
103114
}
104115
}

0 commit comments

Comments
 (0)