Skip to content

Commit ca8d557

Browse files
committed
Merge remote-tracking branch 'act4/ACP2E-3399' into pr_january_doleksandr
2 parents 867c534 + b1e5b17 commit ca8d557

File tree

19 files changed

+717
-310
lines changed

19 files changed

+717
-310
lines changed

app/code/Magento/GraphQl/Helper/Error/AggregateExceptionMessageFormatter.php

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

@@ -23,7 +23,7 @@ class AggregateExceptionMessageFormatter
2323
/**
2424
* @var ExceptionMessageFormatterInterface[]
2525
*/
26-
private $messageFormatters;
26+
private array $messageFormatters;
2727

2828
/**
2929
* @param ExceptionMessageFormatterInterface[] $messageFormatters
@@ -54,11 +54,12 @@ public function getFormatted(
5454
ResolveInfo $info
5555
): ClientAware {
5656
foreach ($this->messageFormatters as $formatter) {
57-
$formatted = $formatter->getFormatted($e, $messagePrefix, $field, $context, $info);
58-
if ($formatted) {
57+
if ($formatted = $formatter->getFormatted($e, $messagePrefix, $field, $context, $info)) {
5958
return $formatted;
6059
}
6160
}
62-
return new GraphQlInputException($defaultMessage, $e);
61+
62+
$message = $e->getCode() ? __($e->getMessage()) : $defaultMessage;
63+
return new GraphQlInputException($message, $e, $e->getCode());
6364
}
6465
}
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
<?php
2+
/**
3+
* Copyright 2024 Adobe
4+
* All Rights Reserved.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\GraphQl\Test\Unit\Helper\Error;
9+
10+
use GraphQL\Error\ClientAware;
11+
use Magento\Framework\Exception\LocalizedException;
12+
use Magento\Framework\GraphQl\Config\Element\Field;
13+
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
14+
use Magento\Framework\GraphQl\Query\Resolver\ContextInterface;
15+
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
16+
use Magento\Framework\Phrase;
17+
use Magento\GraphQl\Helper\Error\AggregateExceptionMessageFormatter;
18+
use Magento\GraphQl\Helper\Error\ExceptionMessageFormatterInterface;
19+
use PHPUnit\Framework\MockObject\Exception;
20+
use PHPUnit\Framework\MockObject\MockObject;
21+
use PHPUnit\Framework\TestCase;
22+
23+
class AggregateExceptionMessageFormatterTest extends TestCase
24+
{
25+
/**
26+
* @var ExceptionMessageFormatterInterface|MockObject
27+
*/
28+
private ExceptionMessageFormatterInterface $formatter;
29+
30+
/**
31+
* @var LocalizedException|LocalizedException&MockObject|MockObject
32+
*/
33+
private LocalizedException $exception;
34+
35+
/**
36+
* @var Phrase|Phrase&MockObject|MockObject
37+
*/
38+
private Phrase $phrase;
39+
40+
/**
41+
* @var Field|Field&MockObject|MockObject
42+
*/
43+
private Field $field;
44+
45+
/**
46+
* @var ContextInterface|ContextInterface&MockObject|MockObject
47+
*/
48+
private ContextInterface $context;
49+
50+
/**
51+
* @var ResolveInfo|ResolveInfo&MockObject|MockObject
52+
*/
53+
private ResolveInfo $info;
54+
55+
/**
56+
* @return void
57+
* @throws Exception
58+
*/
59+
protected function setUp(): void
60+
{
61+
$this->formatter = $this->createMock(ExceptionMessageFormatterInterface::class);
62+
$this->exception = $this->createMock(LocalizedException::class);
63+
$this->phrase = $this->createMock(Phrase::class);
64+
$this->field = $this->createMock(Field::class);
65+
$this->context = $this->createMock(ContextInterface::class);
66+
$this->info = $this->createMock(ResolveInfo::class);
67+
68+
parent::setUp();
69+
}
70+
71+
/**
72+
* @return void
73+
* @throws Exception
74+
*/
75+
public function testGetFormattedUsingFormatter(): void
76+
{
77+
$clientAware = $this->createMock(ClientAware::class);
78+
$this->formatter->expects($this->once())
79+
->method('getFormatted')
80+
->willReturn($clientAware);
81+
$messagePrefix = 'prefix';
82+
83+
$aggregateFormatter = new AggregateExceptionMessageFormatter([$this->formatter]);
84+
$this->assertSame(
85+
$clientAware,
86+
$aggregateFormatter->getFormatted(
87+
$this->exception,
88+
$this->phrase,
89+
$messagePrefix,
90+
$this->field,
91+
$this->context,
92+
$this->info
93+
)
94+
);
95+
}
96+
97+
/**
98+
* @return void
99+
*/
100+
public function testGetFormattedExceptionMessage(): void
101+
{
102+
$exceptionCode = 1;
103+
$exceptionMessage = 'exception message';
104+
$messagePrefix = 'prefix';
105+
$this->formatter->expects($this->once())
106+
->method('getFormatted')
107+
->willReturn(null);
108+
$paramException = new LocalizedException(__($exceptionMessage), null, $exceptionCode);
109+
110+
$aggregateFormatter = new AggregateExceptionMessageFormatter([$this->formatter]);
111+
$exception = $aggregateFormatter->getFormatted(
112+
$paramException,
113+
$this->phrase,
114+
$messagePrefix,
115+
$this->field,
116+
$this->context,
117+
$this->info
118+
);
119+
$this->assertInstanceOf(GraphQlInputException::class, $exception);
120+
$this->assertSame($exceptionCode, $exception->getCode());
121+
$this->assertSame($exceptionMessage, $exception->getMessage());
122+
}
123+
124+
/**
125+
* @return void
126+
*/
127+
public function testGetFormattedDefaultMessage(): void
128+
{
129+
$exceptionMessage = 'exception message';
130+
$messagePrefix = 'prefix';
131+
$this->formatter->expects($this->once())
132+
->method('getFormatted')
133+
->willReturn(null);
134+
$paramException = new LocalizedException(__($exceptionMessage));
135+
136+
$this->phrase->expects($this->once())
137+
->method('render')
138+
->willReturn('prefix default');
139+
$aggregateFormatter = new AggregateExceptionMessageFormatter([$this->formatter]);
140+
$exception = $aggregateFormatter->getFormatted(
141+
$paramException,
142+
$this->phrase,
143+
$messagePrefix,
144+
$this->field,
145+
$this->context,
146+
$this->info
147+
);
148+
$this->assertInstanceOf(GraphQlInputException::class, $exception);
149+
$this->assertSame('prefix default', $exception->getMessage());
150+
}
151+
}

app/code/Magento/QuoteGraphQl/Model/Cart/GetCartForUser.php

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

@@ -14,6 +14,7 @@
1414
use Magento\Quote\Api\CartRepositoryInterface;
1515
use Magento\Quote\Model\MaskedQuoteIdToQuoteIdInterface;
1616
use Magento\Quote\Model\Quote;
17+
use Magento\QuoteGraphQl\Model\ErrorMapper;
1718

1819
/**
1920
* Get cart
@@ -23,39 +24,47 @@ class GetCartForUser
2324
/**
2425
* @var MaskedQuoteIdToQuoteIdInterface
2526
*/
26-
private $maskedQuoteIdToQuoteId;
27+
private MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId;
2728

2829
/**
2930
* @var CartRepositoryInterface
3031
*/
31-
private $cartRepository;
32+
private CartRepositoryInterface $cartRepository;
3233

3334
/**
3435
* @var IsActive
3536
*/
36-
private $isActive;
37+
private IsActive $isActive;
3738

3839
/**
3940
* @var UpdateCartCurrency
4041
*/
41-
private $updateCartCurrency;
42+
private UpdateCartCurrency $updateCartCurrency;
43+
44+
/**
45+
* @var ErrorMapper
46+
*/
47+
private ErrorMapper $errorMapper;
4248

4349
/**
4450
* @param MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId
4551
* @param CartRepositoryInterface $cartRepository
4652
* @param IsActive $isActive
4753
* @param UpdateCartCurrency $updateCartCurrency
54+
* @param ErrorMapper $errorMapper
4855
*/
4956
public function __construct(
5057
MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId,
5158
CartRepositoryInterface $cartRepository,
5259
IsActive $isActive,
53-
UpdateCartCurrency $updateCartCurrency
60+
UpdateCartCurrency $updateCartCurrency,
61+
ErrorMapper $errorMapper
5462
) {
5563
$this->maskedQuoteIdToQuoteId = $maskedQuoteIdToQuoteId;
5664
$this->cartRepository = $cartRepository;
5765
$this->isActive = $isActive;
5866
$this->updateCartCurrency = $updateCartCurrency;
67+
$this->errorMapper = $errorMapper;
5968
}
6069

6170
/**
@@ -78,12 +87,18 @@ public function execute(string $cartHash, ?int $customerId, int $storeId): Quote
7887
$cart = $this->cartRepository->get($cartId);
7988
} catch (NoSuchEntityException $exception) {
8089
throw new GraphQlNoSuchEntityException(
81-
__('Could not find a cart with ID "%masked_cart_id"', ['masked_cart_id' => $cartHash])
90+
__('Could not find a cart with ID "%masked_cart_id"', ['masked_cart_id' => $cartHash]),
91+
$exception,
92+
$this->errorMapper->getErrorMessageId('Could not find a cart with ID')
8293
);
8394
}
8495

8596
if (false === (bool)$this->isActive->execute($cart)) {
86-
throw new GraphQlNoSuchEntityException(__('The cart isn\'t active.'));
97+
throw new GraphQlNoSuchEntityException(
98+
__('The cart isn\'t active.'),
99+
null,
100+
$this->errorMapper->getErrorMessageId('The cart isn\'t active')
101+
);
87102
}
88103

89104
$cart = $this->updateCartCurrency->execute($cart, $storeId);
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
<?php
2+
/**
3+
* Copyright 2024 Adobe
4+
* All Rights Reserved.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\QuoteGraphQl\Model;
9+
10+
class ErrorMapper
11+
{
12+
/**
13+
* Error message codes
14+
*/
15+
public const ERROR_CART_NOT_FOUND = 'CART_NOT_FOUND';
16+
public const ERROR_CART_NOT_ACTIVE = 'CART_NOT_ACTIVE';
17+
public const ERROR_GUEST_EMAIL_MISSING = 'GUEST_EMAIL_MISSING';
18+
public const ERROR_UNABLE_TO_PLACE_ORDER = 'UNABLE_TO_PLACE_ORDER';
19+
public const ERROR_UNDEFINED = 'UNDEFINED';
20+
21+
/**
22+
* Error message codes ids
23+
*/
24+
public const ERROR_CART_NOT_FOUND_ID = 1001;
25+
public const ERROR_CART_NOT_ACTIVE_ID = 1002;
26+
public const ERROR_GUEST_EMAIL_MISSING_ID = 1003;
27+
public const ERROR_UNABLE_TO_PLACE_ORDER_ID = 1004;
28+
public const ERROR_UNDEFINED_ID = 1005;
29+
30+
/**
31+
* List of error messages and codes ids.
32+
*/
33+
public const MESSAGE_IDS = [
34+
'Could not find a cart with ID' => self::ERROR_CART_NOT_FOUND_ID,
35+
'The cart isn\'t active' => self::ERROR_CART_NOT_ACTIVE_ID,
36+
'Guest email for cart is missing' => self::ERROR_GUEST_EMAIL_MISSING_ID,
37+
'A server error stopped your order from being placed. Please try to place your order again' =>
38+
self::ERROR_UNABLE_TO_PLACE_ORDER_ID,
39+
'Some addresses can\'t be used due to the configurations for specific countries' =>
40+
self::ERROR_UNABLE_TO_PLACE_ORDER_ID,
41+
'The shipping method is missing. Select the shipping method and try again' =>
42+
self::ERROR_UNABLE_TO_PLACE_ORDER_ID,
43+
'Please check the billing address information' => self::ERROR_UNABLE_TO_PLACE_ORDER_ID,
44+
'Enter a valid payment method and try again' => self::ERROR_UNABLE_TO_PLACE_ORDER_ID,
45+
'Some of the products are out of stock' => self::ERROR_UNABLE_TO_PLACE_ORDER_ID,
46+
];
47+
48+
/**
49+
* List of error message ids and codes.
50+
*/
51+
public const MESSAGE_CODE_IDS = [
52+
self::ERROR_CART_NOT_FOUND_ID => self::ERROR_CART_NOT_FOUND,
53+
self::ERROR_CART_NOT_ACTIVE_ID => self::ERROR_CART_NOT_ACTIVE,
54+
self::ERROR_GUEST_EMAIL_MISSING_ID => self::ERROR_GUEST_EMAIL_MISSING,
55+
self::ERROR_UNABLE_TO_PLACE_ORDER_ID => self::ERROR_UNABLE_TO_PLACE_ORDER,
56+
self::ERROR_UNDEFINED_ID => self::ERROR_UNDEFINED
57+
];
58+
59+
/**
60+
* List of error messages and codes.
61+
*/
62+
public const MESSAGE_CODES = [
63+
'Could not find a cart with ID' => self::ERROR_CART_NOT_FOUND,
64+
'The cart isn\'t active' => self::ERROR_CART_NOT_ACTIVE,
65+
'Guest email for cart is missing' => self::ERROR_GUEST_EMAIL_MISSING,
66+
'A server error stopped your order from being placed. Please try to place your order again' =>
67+
self::ERROR_UNABLE_TO_PLACE_ORDER,
68+
'Some addresses can\'t be used due to the configurations for specific countries' =>
69+
self::ERROR_UNABLE_TO_PLACE_ORDER,
70+
'The shipping method is missing. Select the shipping method and try again' =>
71+
self::ERROR_UNABLE_TO_PLACE_ORDER,
72+
'Please check the billing address information' => self::ERROR_UNABLE_TO_PLACE_ORDER,
73+
'Enter a valid payment method and try again' => self::ERROR_UNABLE_TO_PLACE_ORDER,
74+
'Some of the products are out of stock' => self::ERROR_UNABLE_TO_PLACE_ORDER,
75+
];
76+
77+
/**
78+
* Transforms a message into a corresponding id
79+
*
80+
* @param string $message
81+
* @return int
82+
*/
83+
public function getErrorMessageId(string $message): int
84+
{
85+
$code = self::ERROR_UNDEFINED_ID;
86+
87+
$matchedCodes = array_filter(
88+
self::MESSAGE_IDS,
89+
function ($key) use ($message) {
90+
return str_contains($message, $key);
91+
},
92+
ARRAY_FILTER_USE_KEY
93+
);
94+
95+
if (!empty($matchedCodes)) {
96+
$code = current($matchedCodes);
97+
}
98+
99+
return $code;
100+
}
101+
}

0 commit comments

Comments
 (0)