diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/GuestEmail.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/GuestEmail.php new file mode 100644 index 0000000000000..76493c4cebe55 --- /dev/null +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/GuestEmail.php @@ -0,0 +1,49 @@ +getCartForUser = $getCartForUser; + } + + /** + * @inheritdoc + */ + public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) + { + if (!isset($value['model'])) { + throw new LocalizedException(__('"model" value should be specified')); + } + /** @var Quote $cart */ + $cart = $value['model']; + + return $cart->getCustomerEmail(); + } +} diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/SetGuestEmailOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetGuestEmailOnCart.php new file mode 100644 index 0000000000000..29dc0e759242f --- /dev/null +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetGuestEmailOnCart.php @@ -0,0 +1,89 @@ +getCartForUser = $getCartForUser; + $this->cartRepository = $cartRepository; + } + + /** + * @inheritdoc + */ + public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) + { + if (!isset($args['input']['cart_id']) || empty($args['input']['cart_id'])) { + throw new GraphQlInputException(__('Required parameter "cart_id" is missing')); + } + $maskedCartId = $args['input']['cart_id']; + + if (!isset($args['input']['email']) || empty($args['input']['email'])) { + throw new GraphQlInputException(__('Required parameter "email" is missing')); + } + + $guestEmail = $args['input']['email']; + + if (!\Zend_Validate::is($guestEmail, EmailAddressValidator::class)) { + throw new GraphQlInputException(__('Invalid email format')); + } + + $currentUserId = $context->getUserId(); + + if ($currentUserId !== 0) { + throw new GraphQlInputException(__('The request is not allowed for logged in customers')); + } + + $cart = $this->getCartForUser->execute($maskedCartId, $currentUserId); + $cart->setCustomerEmail($guestEmail); + + try { + $this->cartRepository->save($cart); + } catch (CouldNotSaveException $e) { + throw new LocalizedException(__($e->getMessage()), $e); + } + + return [ + 'cart' => [ + 'model' => $cart, + 'guest_email' => $guestEmail + ], + ]; + } +} diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index 79cea3855f6f3..bf0d1cfb079cd 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -17,6 +17,7 @@ type Mutation { setBillingAddressOnCart(input: SetBillingAddressOnCartInput): SetBillingAddressOnCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\SetBillingAddressOnCart") setShippingMethodsOnCart(input: SetShippingMethodsOnCartInput): SetShippingMethodsOnCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\SetShippingMethodsOnCart") setPaymentMethodOnCart(input: SetPaymentMethodOnCartInput): SetPaymentMethodOnCartOutput @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\SetPaymentMethodOnCart") + setGuestEmailOnCart(input: SetGuestEmailOnCartInput): SetGuestEmailOnCartOutput @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\SetGuestEmailOnCart") } input AddSimpleProductsToCartInput { @@ -124,6 +125,11 @@ input PaymentMethodInput { purchase_order_number: String @doc(description:"Purchase order number") } +input SetGuestEmailOnCartInput { + cart_id: String! + email: String! +} + type SetPaymentMethodOnCartOutput { cart: Cart! } @@ -147,6 +153,7 @@ type ApplyCouponToCartOutput { type Cart { items: [CartItemInterface] @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\CartItems") applied_coupon: AppliedCoupon @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\AppliedCoupon") + guest_email: String @resolver (class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\GuestEmail") shipping_addresses: [CartAddress]! @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\ShippingAddresses") billing_address: CartAddress! @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\BillingAddress") available_payment_methods: [AvailablePaymentMethod] @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\AvailablePaymentMethods") @doc(description: "Available payment methods") @@ -249,6 +256,10 @@ type RemoveItemFromCartOutput { cart: Cart! } +type SetGuestEmailOnCartOutput { + cart: Cart! +} + type SimpleCartItem implements CartItemInterface @doc(description: "Simple Cart Item") { customizable_options: [SelectedCustomizableOption] @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\CustomizableOptions") } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetGuestEmailOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetGuestEmailOnCartTest.php new file mode 100644 index 0000000000000..4dca82118c1ef --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetGuestEmailOnCartTest.php @@ -0,0 +1,85 @@ +getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class); + $this->customerTokenService = $objectManager->get(CustomerTokenServiceInterface::class); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + */ + public function testSetGuestEmailOnCartForLoggedInCustomer() + { + $reservedOrderId = 'test_order_1'; + $email = 'some@user.com'; + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute($reservedOrderId); + + $query = $this->getSetGuestEmailOnCartMutation($maskedQuoteId, $email); + $this->expectExceptionMessage('The request is not allowed for logged in customers'); + $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + } + + /** + * Returns GraphQl mutation query for setting email address for a guest + * + * @param string $maskedQuoteId + * @param string $email + * @return string + */ + private function getSetGuestEmailOnCartMutation(string $maskedQuoteId, string $email): string + { + return <<customerTokenService->createCustomerAccessToken($username, $password); + $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; + return $headerMap; + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/CartGuestEmailTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/CartGuestEmailTest.php new file mode 100644 index 0000000000000..751029e6b87a3 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/CartGuestEmailTest.php @@ -0,0 +1,52 @@ +getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_virtual_product_saved.php + */ + public function testGetCartGuestEmail() + { + $email = 'store@example.com'; + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute( + 'test_order_with_virtual_product_without_address' + ); + + $query = <<graphQlQuery($query); + $this->assertArrayHasKey('cart', $response); + $this->assertEquals($email, $response['cart']['guest_email']); + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetGuestEmailOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetGuestEmailOnCartTest.php new file mode 100644 index 0000000000000..f95679b2eb5e1 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetGuestEmailOnCartTest.php @@ -0,0 +1,101 @@ +getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class); + } + + /** + * @magentoApiDataFixture Magento/Quote/_files/empty_quote.php + */ + public function testSetGuestEmailOnCart() + { + $reservedOrderId = 'reserved_order_id'; + $email = 'some@user.com'; + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute($reservedOrderId); + + $query = $this->getSetGuestEmailOnCartMutation($maskedQuoteId, $email); + $response = $this->graphQlQuery($query); + + $this->assertArrayHasKey('setGuestEmailOnCart', $response); + $this->assertArrayHasKey('cart', $response['setGuestEmailOnCart']); + $this->assertEquals($email, $response['setGuestEmailOnCart']['cart']['guest_email']); + } + + /** + * @magentoApiDataFixture Magento/Quote/_files/empty_quote.php + * @dataProvider incorrectInputDataProvider + * @param string|null $maskedQuoteId + * @param string $email + * @param string $exceptionMessage + */ + public function testSetGuestEmailOnCartWithIncorrectInputData( + ?string $maskedQuoteId, + string $email, + string $exceptionMessage + ) { + if (null === $maskedQuoteId) { // Generate ID in case if no provided by data provider + $reservedOrderId = 'reserved_order_id'; + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute($reservedOrderId); + } + + $query = $this->getSetGuestEmailOnCartMutation($maskedQuoteId, $email); + $this->expectExceptionMessage($exceptionMessage); + $this->graphQlQuery($query); + } + + public function incorrectInputDataProvider(): array + { + return [ + 'wrong_email' => [null, 'some', 'Invalid email format'], + 'no_email' => [null, '', 'Required parameter "email" is missing'], + 'wrong_quote_id' => ['xxx', 'some@user.com', 'Could not find a cart with ID "xxx"'], + 'no_quote_id' => ['', 'some@user.com', 'Required parameter "cart_id" is missing'] + ]; + } + + /** + * Returns GraphQl mutation query for setting email address for a guest + * + * @param string $maskedQuoteId + * @param string $email + * @return string + */ + private function getSetGuestEmailOnCartMutation(string $maskedQuoteId, string $email): string + { + return <<setIsMultiShipping(false) ->setReservedOrderId('test_order_with_virtual_product_without_address') ->setEmail('store@example.com') + ->setCustomerEmail('store@example.com') ->addProduct( $product->load($product->getId()), 1