Skip to content

Commit 8e3a2e0

Browse files
eliseacornejosvera
andauthored
LYNX-115: Add specific operation error codes to the "placeOrder" mutation (#203)
Co-authored-by: Sergio Vera <[email protected]>
1 parent 9263e99 commit 8e3a2e0

File tree

11 files changed

+844
-440
lines changed

11 files changed

+844
-440
lines changed

app/code/Magento/Catalog/Test/Fixture/ProductStock.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ class ProductStock implements DataFixtureInterface
2626
{
2727
private const DEFAULT_DATA = [
2828
'prod_id' => null,
29-
'prod_qty' => 1
29+
'prod_qty' => 1,
30+
'is_in_stock' => 1
3031
];
3132

3233
/**
@@ -67,8 +68,8 @@ public function apply(array $data = []): ?DataObject
6768
{
6869
$data = $this->dataMerger->merge(self::DEFAULT_DATA, $data);
6970
$stockItem = $this->stockRegistry->getStockItem($data['prod_id']);
70-
$stockItem->setData('is_in_stock', 1);
71-
$stockItem->setData('qty', 90);
71+
$stockItem->setData('is_in_stock', $data['is_in_stock']);
72+
$stockItem->setData('qty', $data['prod_qty']);
7273
$stockItem->setData('manage_stock', 1);
7374
$stockItem->save();
7475

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?php
2+
/**
3+
* Copyright 2023 Adobe
4+
* All Rights Reserved.
5+
*
6+
* NOTICE: All information contained herein is, and remains
7+
* the property of Adobe and its suppliers, if any. The intellectual
8+
* and technical concepts contained herein are proprietary to Adobe
9+
* and its suppliers and are protected by all applicable intellectual
10+
* property laws, including trade secret and copyright laws.
11+
* Dissemination of this information or reproduction of this material
12+
* is strictly forbidden unless prior written permission is obtained from
13+
* Adobe.
14+
*/
15+
declare(strict_types=1);
16+
17+
namespace Magento\GiftMessage\Test\Fixture;
18+
19+
use Magento\Framework\DataObject;
20+
use Magento\TestFramework\Fixture\Api\DataMerger;
21+
use Magento\GiftMessage\Model\ResourceModel\Message;
22+
use Magento\GiftMessage\Model\MessageFactory;
23+
use Magento\TestFramework\Fixture\RevertibleDataFixtureInterface;
24+
25+
class GiftMessage implements RevertibleDataFixtureInterface
26+
{
27+
private const DEFAULT_DATA = [
28+
'sender' => 'Romeo',
29+
'recipient' => 'Mercutio',
30+
'message' => 'Fixture Test message.',
31+
];
32+
33+
/**
34+
* @param MessageFactory $giftMessageFactory
35+
* @param Message $messageResourceModel
36+
* @param DataMerger $dataMerger
37+
*/
38+
public function __construct(
39+
private readonly MessageFactory $giftMessageFactory,
40+
private readonly Message $messageResourceModel,
41+
private readonly DataMerger $dataMerger,
42+
) {
43+
}
44+
45+
/**
46+
* @inheritdoc
47+
*/
48+
public function apply(array $data = []): ?DataObject
49+
{
50+
$data = $this->dataMerger->merge(self::DEFAULT_DATA, $data);
51+
$message = $this->giftMessageFactory->create();
52+
$message
53+
->setSender($data['sender'])
54+
->setRecipient($data['recipient'])
55+
->setMessage($data['message']);
56+
57+
$this->messageResourceModel->save($message);
58+
59+
return $message;
60+
}
61+
62+
/**
63+
* @inheritdoc
64+
*/
65+
public function revert(DataObject $data): void
66+
{
67+
$this->messageResourceModel->delete($data);
68+
}
69+
}

app/code/Magento/Quote/Test/Fixture/CustomerCart.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
class CustomerCart implements RevertibleDataFixtureInterface
1616
{
1717
private const DEFAULT_DATA = [
18-
'customer_id' => null
18+
'customer_id' => null,
19+
'reserved_order_id' => null
1920
];
2021

2122
/**
@@ -53,8 +54,13 @@ public function apply(array $data = []): ?DataObject
5354
{
5455
$data = array_merge(self::DEFAULT_DATA, $data);
5556
$cartId = $this->cartManagement->createEmptyCartForCustomer($data['customer_id']);
57+
$cart = $this->cartRepository->get($cartId);
58+
if (isset($data['reserved_order_id'])) {
59+
$cart->setReservedOrderId($data['reserved_order_id']);
60+
$this->cartRepository->save($cart);
61+
}
5662

57-
return $this->cartRepository->get($cartId);
63+
return $cart;
5864
}
5965

6066
/**

app/code/Magento/Quote/Test/Fixture/GuestCart.php

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,21 @@ public function apply(array $data = []): ?DataObject
7171
{
7272
$maskId = $this->guestCartManagement->createEmptyCart();
7373
$cartId = $this->maskedQuoteIdToQuoteId->execute($maskId);
74+
$cart = $this->cartRepository->get($cartId);
7475

75-
return $this->cartRepository->get($cartId);
76+
if (!isset($data['reserved_order_id']) && !isset($data['message_id'])) {
77+
return $cart;
78+
}
79+
if (isset($data['reserved_order_id'])) {
80+
$cart->setReservedOrderId($data['reserved_order_id']);
81+
$this->cartRepository->save($cart);
82+
}
83+
if (isset($data['message_id'])) {
84+
$cart->setGiftMessageId($data['message_id']);
85+
$this->cartRepository->save($cart);
86+
}
87+
88+
return $cart;
7689
}
7790

7891
/**

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

Lines changed: 95 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,50 @@
99

1010
use Magento\Framework\Exception\AuthorizationException;
1111
use Magento\Framework\Exception\LocalizedException;
12+
use Magento\Framework\Exception\NoSuchEntityException;
1213
use Magento\Framework\GraphQl\Config\Element\Field;
1314
use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException;
1415
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
1516
use Magento\Framework\GraphQl\Query\ResolverInterface;
1617
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
17-
use Magento\GraphQl\Helper\Error\AggregateExceptionMessageFormatter;
18+
use Magento\Framework\ObjectManager\ResetAfterRequestInterface;
1819
use Magento\QuoteGraphQl\Model\Cart\GetCartForCheckout;
19-
use Magento\GraphQl\Model\Query\ContextInterface;
2020
use Magento\QuoteGraphQl\Model\Cart\PlaceOrder as PlaceOrderModel;
2121
use Magento\Sales\Api\OrderRepositoryInterface;
2222

2323
/**
2424
* Resolver for placing order after payment method has already been set
2525
*/
26-
class PlaceOrder implements ResolverInterface
26+
class PlaceOrder implements ResolverInterface, ResetAfterRequestInterface
2727
{
28+
/**#@+
29+
* Error message codes
30+
*/
31+
private const ERROR_CART_NOT_FOUND = 'CART_NOT_FOUND';
32+
private const ERROR_CART_NOT_ACTIVE = 'CART_NOT_ACTIVE';
33+
private const ERROR_GUEST_EMAIL_MISSING = 'GUEST_EMAIL_MISSING';
34+
private const ERROR_UNABLE_TO_PLACE_ORDER = 'UNABLE_TO_PLACE_ORDER';
35+
private const ERROR_UNDEFINED = 'UNDEFINED';
36+
/**#@-*/
37+
38+
/**
39+
* List of error messages and codes.
40+
*/
41+
private const MESSAGE_CODES = [
42+
'Could not find a cart with ID' => self::ERROR_CART_NOT_FOUND,
43+
'The cart isn\'t active' => self::ERROR_CART_NOT_ACTIVE,
44+
'Guest email for cart is missing' => self::ERROR_GUEST_EMAIL_MISSING,
45+
'A server error stopped your order from being placed. Please try to place your order again' =>
46+
self::ERROR_UNABLE_TO_PLACE_ORDER,
47+
'Some addresses can\'t be used due to the configurations for specific countries' =>
48+
self::ERROR_UNABLE_TO_PLACE_ORDER,
49+
'The shipping method is missing. Select the shipping method and try again' =>
50+
self::ERROR_UNABLE_TO_PLACE_ORDER,
51+
'Please check the billing address information' => self::ERROR_UNABLE_TO_PLACE_ORDER,
52+
'Enter a valid payment method and try again' => self::ERROR_UNABLE_TO_PLACE_ORDER,
53+
'Some of the products are out of stock' => self::ERROR_UNABLE_TO_PLACE_ORDER,
54+
];
55+
2856
/**
2957
* @var GetCartForCheckout
3058
*/
@@ -41,33 +69,32 @@ class PlaceOrder implements ResolverInterface
4169
private $orderRepository;
4270

4371
/**
44-
* @var AggregateExceptionMessageFormatter
72+
* @var \string[]
4573
*/
46-
private $errorMessageFormatter;
74+
private $errors = [];
4775

4876
/**
4977
* @param GetCartForCheckout $getCartForCheckout
5078
* @param PlaceOrderModel $placeOrder
5179
* @param OrderRepositoryInterface $orderRepository
52-
* @param AggregateExceptionMessageFormatter $errorMessageFormatter
5380
*/
5481
public function __construct(
5582
GetCartForCheckout $getCartForCheckout,
5683
PlaceOrderModel $placeOrder,
57-
OrderRepositoryInterface $orderRepository,
58-
AggregateExceptionMessageFormatter $errorMessageFormatter
84+
OrderRepositoryInterface $orderRepository
5985
) {
6086
$this->getCartForCheckout = $getCartForCheckout;
6187
$this->placeOrder = $placeOrder;
6288
$this->orderRepository = $orderRepository;
63-
$this->errorMessageFormatter = $errorMessageFormatter;
6489
}
6590

6691
/**
6792
* @inheritdoc
6893
*/
6994
public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null)
7095
{
96+
$this->errors = [];
97+
$order = null;
7198
if (empty($args['input']['cart_id'])) {
7299
throw new GraphQlInputException(__('Required parameter "cart_id" is missing'));
73100
}
@@ -80,28 +107,77 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value
80107
$cart = $this->getCartForCheckout->execute($maskedCartId, $userId, $storeId);
81108
$orderId = $this->placeOrder->execute($cart, $maskedCartId, $userId);
82109
$order = $this->orderRepository->get($orderId);
110+
} catch (NoSuchEntityException $exception) {
111+
$this->addError($exception->getMessage());
112+
} catch (GraphQlInputException $exception) {
113+
$this->addError($exception->getMessage());
83114
} catch (AuthorizationException $exception) {
84115
throw new GraphQlAuthorizationException(
85116
__($exception->getMessage())
86117
);
87118
} catch (LocalizedException $e) {
88-
throw $this->errorMessageFormatter->getFormatted(
89-
$e,
90-
__('Unable to place order: A server error stopped your order from being placed. ' .
91-
'Please try to place your order again'),
92-
'Unable to place order',
93-
$field,
94-
$context,
95-
$info
96-
);
119+
$this->addError($e->getMessage());
120+
}
121+
if ($this->errors) {
122+
return [
123+
'errors' =>
124+
$this->errors
125+
];
97126
}
98-
99127
return [
100128
'order' => [
101129
'order_number' => $order->getIncrementId(),
102130
// @deprecated The order_id field is deprecated, use order_number instead
103131
'order_id' => $order->getIncrementId(),
104132
],
133+
'errors' => []
134+
];
135+
}
136+
137+
/**
138+
* Add order line item error
139+
*
140+
* @param string $message
141+
* @return void
142+
*/
143+
private function addError(string $message): void
144+
{
145+
$this->errors[] = [
146+
'message' => $message,
147+
'code' => $this->getErrorCode($message)
105148
];
106149
}
150+
151+
/**
152+
* Get message error code. Ad-hoc solution based on message parsing.
153+
*
154+
* @param string $message
155+
* @return string
156+
*/
157+
private function getErrorCode(string $message): string
158+
{
159+
$code = self::ERROR_UNDEFINED;
160+
161+
$matchedCodes = array_filter(
162+
self::MESSAGE_CODES,
163+
function ($key) use ($message) {
164+
return false !== strpos($message, $key);
165+
},
166+
ARRAY_FILTER_USE_KEY
167+
);
168+
169+
if (!empty($matchedCodes)) {
170+
$code = current($matchedCodes);
171+
}
172+
173+
return $code;
174+
}
175+
176+
/**
177+
* @inheritDoc
178+
*/
179+
public function _resetState(): void
180+
{
181+
$this->errors = [];
182+
}
107183
}

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

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,13 @@ type ApplyCouponToCartOutput @doc(description: "Contains details about the cart
223223
}
224224

225225
type PlaceOrderOutput @doc(description: "Contains the results of the request to place an order.") {
226-
order: Order! @doc(description: "The ID of the order.")
226+
order: Order @doc(description: "The ID of the order.")
227+
errors: [PlaceOrderError!]! @doc(description:"An array of place order errors.")
228+
}
229+
230+
type PlaceOrderError @doc(description:"An error encountered while placing an order."){
231+
message: String! @doc(description: "A localized error message.")
232+
code: PlaceOrderErrorCodes! @doc(description: "An error code that is specific to place order.")
227233
}
228234

229235
type Cart @doc(description: "Contains the contents and other details about a guest or customer cart.") {
@@ -456,6 +462,13 @@ enum CartUserInputErrorType {
456462
INSUFFICIENT_STOCK
457463
UNDEFINED
458464
}
465+
enum PlaceOrderErrorCodes {
466+
CART_NOT_FOUND
467+
CART_NOT_ACTIVE
468+
GUEST_EMAIL_MISSING
469+
UNABLE_TO_PLACE_ORDER
470+
UNDEFINED
471+
}
459472

460473
type StoreConfig {
461474
is_guest_checkout_enabled: Boolean @doc(description: "Extended Config Data - checkout/options/guest_checkout")

0 commit comments

Comments
 (0)