diff --git a/vendor/magento/module-quote-graph-ql/Model/Resolver/PlaceOrder.php b/vendor/magento/module-quote-graph-ql/Model/Resolver/PlaceOrder.php index 48346918b31cf..7cbc64a41d37c 100644 --- a/vendor/magento/module-quote-graph-ql/Model/Resolver/PlaceOrder.php +++ b/vendor/magento/module-quote-graph-ql/Model/Resolver/PlaceOrder.php @@ -7,16 +7,17 @@ namespace Magento\QuoteGraphQl\Model\Resolver; -use Magento\Framework\Exception\AuthorizationException; +use Magento\Framework\App\ObjectManager; use Magento\Framework\Exception\LocalizedException; -use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\GraphQl\Config\Element\Field; -use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\GraphQl\Helper\Error\AggregateExceptionMessageFormatter; use Magento\QuoteGraphQl\Model\Cart\GetCartForCheckout; +use Magento\GraphQl\Model\Query\ContextInterface; use Magento\QuoteGraphQl\Model\Cart\PlaceOrder as PlaceOrderModel; +use Magento\QuoteGraphQl\Model\Cart\PlaceOrderMutexInterface; use Magento\Sales\Api\OrderRepositoryInterface; /** @@ -24,49 +25,50 @@ */ class PlaceOrder implements ResolverInterface { - /**#@+ - * Error message codes + /** + * @var GetCartForCheckout */ - private const ERROR_CART_NOT_FOUND = 'CART_NOT_FOUND'; - private const ERROR_CART_NOT_ACTIVE = 'CART_NOT_ACTIVE'; - private const ERROR_GUEST_EMAIL_MISSING = 'GUEST_EMAIL_MISSING'; - private const ERROR_UNABLE_TO_PLACE_ORDER = 'UNABLE_TO_PLACE_ORDER'; - private const ERROR_UNDEFINED = 'UNDEFINED'; - /**#@-*/ + private $getCartForCheckout; /** - * List of error messages and codes. + * @var PlaceOrderModel */ - private const MESSAGE_CODES = [ - 'Could not find a cart with ID' => self::ERROR_CART_NOT_FOUND, - 'The cart isn\'t active' => self::ERROR_CART_NOT_ACTIVE, - 'Guest email for cart is missing' => self::ERROR_GUEST_EMAIL_MISSING, - 'A server error stopped your order from being placed. Please try to place your order again' => - self::ERROR_UNABLE_TO_PLACE_ORDER, - 'Some addresses can\'t be used due to the configurations for specific countries' => - self::ERROR_UNABLE_TO_PLACE_ORDER, - 'The shipping method is missing. Select the shipping method and try again' => - self::ERROR_UNABLE_TO_PLACE_ORDER, - 'Please check the billing address information' => self::ERROR_UNABLE_TO_PLACE_ORDER, - 'Enter a valid payment method and try again' => self::ERROR_UNABLE_TO_PLACE_ORDER, - 'Some of the products are out of stock' => self::ERROR_UNABLE_TO_PLACE_ORDER, - ]; + private $placeOrder; /** - * @var \string[] + * @var OrderRepositoryInterface */ - private $errors = []; + private $orderRepository; + + /** + * @var AggregateExceptionMessageFormatter + */ + private $errorMessageFormatter; + + /** + * @var PlaceOrderMutexInterface + */ + private $placeOrderMutex; /** * @param GetCartForCheckout $getCartForCheckout * @param PlaceOrderModel $placeOrder * @param OrderRepositoryInterface $orderRepository + * @param AggregateExceptionMessageFormatter $errorMessageFormatter + * @param PlaceOrderMutexInterface|null $placeOrderMutex */ public function __construct( - private readonly GetCartForCheckout $getCartForCheckout, - private readonly PlaceOrderModel $placeOrder, - private readonly OrderRepositoryInterface $orderRepository, + GetCartForCheckout $getCartForCheckout, + PlaceOrderModel $placeOrder, + OrderRepositoryInterface $orderRepository, + AggregateExceptionMessageFormatter $errorMessageFormatter, + ?PlaceOrderMutexInterface $placeOrderMutex = null ) { + $this->getCartForCheckout = $getCartForCheckout; + $this->placeOrder = $placeOrder; + $this->orderRepository = $orderRepository; + $this->errorMessageFormatter = $errorMessageFormatter; + $this->placeOrderMutex = $placeOrderMutex ?: ObjectManager::getInstance()->get(PlaceOrderMutexInterface::class); } /** @@ -74,12 +76,29 @@ public function __construct( */ public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) { - $this->errors = []; - $order = null; if (empty($args['input']['cart_id'])) { throw new GraphQlInputException(__('Required parameter "cart_id" is missing')); } + return $this->placeOrderMutex->execute( + $args['input']['cart_id'], + \Closure::fromCallable([$this, 'run']), + [$field, $context, $info, $args] + ); + } + + /** + * Run the resolver. + * + * @param Field $field + * @param ContextInterface $context + * @param ResolveInfo $info + * @param array|null $args + * @return array[] + * @SuppressWarnings(PHPMD.UnusedPrivateMethod) + */ + private function run(Field $field, ContextInterface $context, ResolveInfo $info, ?array $args): array + { $maskedCartId = $args['input']['cart_id']; $userId = (int)$context->getUserId(); $storeId = (int)$context->getExtensionAttributes()->getStore()->getId(); @@ -88,69 +107,24 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value $cart = $this->getCartForCheckout->execute($maskedCartId, $userId, $storeId); $orderId = $this->placeOrder->execute($cart, $maskedCartId, $userId); $order = $this->orderRepository->get($orderId); - } catch (NoSuchEntityException $exception) { - $this->addError($exception->getMessage()); - } catch (GraphQlInputException $exception) { - $this->addError($exception->getMessage()); - } catch (AuthorizationException $exception) { - throw new GraphQlAuthorizationException( - __($exception->getMessage()) - ); } catch (LocalizedException $e) { - $this->addError($e->getMessage()); - } - if ($this->errors) { - return [ - 'errors' => - $this->errors - ]; + throw $this->errorMessageFormatter->getFormatted( + $e, + __('Unable to place order: A server error stopped your order from being placed. ' . + 'Please try to place your order again'), + 'Unable to place order', + $field, + $context, + $info + ); } + return [ 'order' => [ 'order_number' => $order->getIncrementId(), // @deprecated The order_id field is deprecated, use order_number instead 'order_id' => $order->getIncrementId(), ], - 'errors' => [] ]; } - - /** - * Add order line item error - * - * @param string $message - * @return void - */ - private function addError(string $message): void - { - $this->errors[] = [ - 'message' => $message, - 'code' => $this->getErrorCode($message) - ]; - } - - /** - * Get message error code. Ad-hoc solution based on message parsing. - * - * @param string $message - * @return string - */ - private function getErrorCode(string $message): string - { - $code = self::ERROR_UNDEFINED; - - $matchedCodes = array_filter( - self::MESSAGE_CODES, - function ($key) use ($message) { - return false !== strpos($message, $key); - }, - ARRAY_FILTER_USE_KEY - ); - - if (!empty($matchedCodes)) { - $code = current($matchedCodes); - } - - return $code; - } } diff --git a/vendor/magento/module-quote-graph-ql/etc/schema.graphqls b/vendor/magento/module-quote-graph-ql/etc/schema.graphqls index ffe6655e927e1..27433a30f3c92 100644 --- a/vendor/magento/module-quote-graph-ql/etc/schema.graphqls +++ b/vendor/magento/module-quote-graph-ql/etc/schema.graphqls @@ -202,13 +202,7 @@ type ApplyCouponToCartOutput @doc(description: "Contains details about the cart } type PlaceOrderOutput @doc(description: "Contains the results of the request to place an order.") { - order: Order @doc(description: "The ID of the order.") - errors: [PlaceOrderError!]! @doc(description:"An array of place order errors.") -} - -type PlaceOrderError @doc(description:"An error encountered while placing an order."){ - message: String! @doc(description: "A localized error message.") - code: PlaceOrderErrorCodes! @doc(description: "An error code that is specific to place order.") + order: Order! @doc(description: "The ID of the order.") } type Cart @doc(description: "Contains the contents and other details about a guest or customer cart.") { @@ -423,11 +417,4 @@ enum CartUserInputErrorType { INSUFFICIENT_STOCK UNDEFINED } -enum PlaceOrderErrorCodes { - CART_NOT_FOUND - CART_NOT_ACTIVE - GUEST_EMAIL_MISSING - UNABLE_TO_PLACE_ORDER - UNDEFINED -}