From 93f6e5e17d024e00cea0bcf6254afc47cf53b73a Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza Date: Sat, 22 Sep 2018 11:46:44 +0200 Subject: [PATCH 01/12] Added schema concept for working with cart addresses and shipping methods --- .../Magento/QuoteGraphQl/etc/schema.graphqls | 116 ++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index 06b3328b9e05..b10575171781 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -1,10 +1,82 @@ # Copyright © Magento, Inc. All rights reserved. # See COPYING.txt for license details. +type Query { + getAvailableShippingMethodsOnCart(input: AvailableShippingMethodsOnCartInput): AvailableShippingMethodsOnCartOutput @doc(description:"Returns available shipping methods for cart by address/address_id") +} + type Mutation { createEmptyCart: String @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\Cart\\CreateEmptyCart") @doc(description:"Creates empty shopping cart for guest or logged in user") applyCouponToCart(input: ApplyCouponToCartInput): ApplyCouponToCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\Coupon\\ApplyCouponToCart") removeCouponFromCart(input: RemoveCouponFromCartInput): RemoveCouponFromCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\Coupon\\RemoveCouponFromCart") + setShippingAddressesOnCart(input: SetShippingAddressesOnCartInput): SetShippingAddressesOnCartOutput + setBillingAddressOnCart(input: SetBillingAddressOnCartInput): SetBillingAddressOnCartOutput + setShippingMethodsOnCart(input: SetShippingMethodsOnCartInput): SetShippingMethodsOnCartOutput +} + +input SetShippingAddressesOnCartInput { + cart_id: String! + customer_address_id: Int # Can be provided in one-page checkout and is required for multi-shipping checkout + address: CartAddressInput + cart_items: [CartItemQuantityInput!] +} + +input CartItemQuantityInput { + cart_item_id: Int! + quantity: Float! +} + +input SetBillingAddressOnCartInput { + cart_id: String! + customer_address_id: Int + address: CartAddressInput + # TODO: consider adding "Same as shipping" option +} + +input CartAddressInput { + firstname: String! + lastname: String! + company: String + street: [String!]! + city: String! + region: String + postcode: String + country_code: String! + telephone: String! + save_in_address_book: Boolean! +} + +input SetShippingMethodsOnCartInput { + shipping_methods: [ShippingMethodForAddressInput!]! +} + +input ShippingMethodForAddressInput { + cart_address_id: String! + shipping_method_code: String! +} + +type SetBillingAddressOnCartOutput { + cart: Cart! +} + +type SetShippingAddressesOnCartOutput { + cart: Cart! +} + +type SetShippingMethodsOnCartOutput { + cart: Cart! +} + +# If no address is provided, the system get address assigned to a quote +# If there's no address at all - the system returns all shipping methods +type AvailableShippingMethodsOnCartInput { + cart_id: String! + customer_address_id: Int + address: CartAddressInput +} + +type AvailableShippingMethodsOnCartOutput { + available_shipping_methods: [CheckoutShippingMethod] } input ApplyCouponToCartInput { @@ -18,12 +90,56 @@ type ApplyCouponToCartOutput { type Cart { applied_coupon: AppliedCoupon + addresses: [CartAddress]! } type CartAddress { + firstname: String! + lastname: String! + company: String + street: [String!]! + city: String! + region: CartAddressRegion + postcode: String + country: CartAddressCountry! + telephone: String! + address_type: AdressTypeEnum! + selected_shipping_method: CheckoutShippingMethod + available_shipping_methods: [CheckoutShippingMethod]! + items_weight: Float + customer_notes: String + cart_items: [CartItemQuantity] applied_coupon: AppliedCoupon } +type CartItemQuantity { + cart_item_id: String! + quantity: Float! +} + +type CartAddressRegion { + code: String + label: String +} + +type CartAddressCountry { + code: String + label: String +} + +type CheckoutShippingMethod { + code: String + label: String + free_shipping: Boolean! + error_message: String + # TODO: Add more complex structure for shipping rates +} + +enum AdressTypeEnum { + SHIPPING + BILLING +} + type AppliedCoupon { code: String! } From 3073ed5e8670ab9b095cc6fb6d29d875d4235ce2 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza Date: Wed, 26 Sep 2018 14:31:18 +0200 Subject: [PATCH 02/12] Shipping method set concept --- .../SetShippingMethodsOnCart.php | 105 ++++++++++++++++++ .../Magento/QuoteGraphQl/etc/schema.graphqls | 9 +- 2 files changed, 110 insertions(+), 4 deletions(-) create mode 100644 app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingMethod/SetShippingMethodsOnCart.php diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingMethod/SetShippingMethodsOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingMethod/SetShippingMethodsOnCart.php new file mode 100644 index 000000000000..448bdcbb37a6 --- /dev/null +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingMethod/SetShippingMethodsOnCart.php @@ -0,0 +1,105 @@ +arrayManager = $arrayManager; + $this->maskedQuoteIdToQuoteId = $maskedQuoteIdToQuoteId; + $this->cartRepository = $cartRepository; + } + + /** + * @inheritdoc + */ + public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) + { + $shippingMethods = $this->arrayManager->get('input/shipping_methods', $args); + $maskedCartId = $this->arrayManager->get('input/cart_id', $args); + + if (!$maskedCartId) { + // TODO: throw an exception + } + + if (!$shippingMethods) { + // TODO: throw an exception? + } + + foreach ($shippingMethods as $shippingMethod) { + if (empty($shippingMethod['cart_address_id'])) { + // TODO: throw input exception + } + + if (empty($shippingMethod['shipping_method_code'])) { + // TODO: throw input exception + } + + // TODO: move to a separate class + // TODO: check current customer can apply operations on specified cart + } + + $quoteId = $this->maskedQuoteIdToQuoteId->execute($maskedCartId); + $quote = $this->cartRepository->get($quoteId); // TODO: catch no such entity exception + $this->setShippingMethods($shippingMethods, $quote); + + $quote->collectTotals(); + $quote->save(); + //$this->cartRepository->save($quote); + + return 'Success!'; + } + + private function setShippingMethods($shippingMethods, CartInterface $quote) + { + $addresses = $quote->getAllShippingAddresses(); + /** @var \Magento\Quote\Model\Quote\Address $address */ + foreach ($addresses as $address) { + $addressId = $address->getId(); + $shippingMethodForAddress = array_search($addressId, array_column($shippingMethods, 'cart_address_id')); + if ($shippingMethodForAddress !== false) { + $address->setShippingMethod($shippingMethods[$shippingMethodForAddress]['shipping_method_code']); +// $address->setCollectShippingRates(1); + $address->save(); + } + } + // TODO: make sure that shipping method is assigned for all addresses + } +} \ No newline at end of file diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index b10575171781..027e59c327eb 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -11,7 +11,7 @@ type Mutation { removeCouponFromCart(input: RemoveCouponFromCartInput): RemoveCouponFromCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\Coupon\\RemoveCouponFromCart") setShippingAddressesOnCart(input: SetShippingAddressesOnCartInput): SetShippingAddressesOnCartOutput setBillingAddressOnCart(input: SetBillingAddressOnCartInput): SetBillingAddressOnCartOutput - setShippingMethodsOnCart(input: SetShippingMethodsOnCartInput): SetShippingMethodsOnCartOutput + setShippingMethodsOnCart(input: SetShippingMethodsOnCartInput): SetShippingMethodsOnCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\ShippingMethod\\SetShippingMethodsOnCart") } input SetShippingAddressesOnCartInput { @@ -47,11 +47,12 @@ input CartAddressInput { } input SetShippingMethodsOnCartInput { + cart_id: String! shipping_methods: [ShippingMethodForAddressInput!]! } input ShippingMethodForAddressInput { - cart_address_id: String! + cart_address_id: String! # todo: int? shipping_method_code: String! } @@ -64,12 +65,12 @@ type SetShippingAddressesOnCartOutput { } type SetShippingMethodsOnCartOutput { - cart: Cart! + cart: String } # If no address is provided, the system get address assigned to a quote # If there's no address at all - the system returns all shipping methods -type AvailableShippingMethodsOnCartInput { +input AvailableShippingMethodsOnCartInput { cart_id: String! customer_address_id: Int address: CartAddressInput From 24672336afc2a748ab6f221a534f93ff1e894fdd Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza Date: Wed, 3 Oct 2018 18:00:18 +0200 Subject: [PATCH 03/12] Concept for setting shipping method only for single address checkout --- .../SetShippingMethodsOnCart.php | 111 +++++++++++------- .../Magento/QuoteGraphQl/etc/schema.graphqls | 5 +- 2 files changed, 71 insertions(+), 45 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingMethod/SetShippingMethodsOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingMethod/SetShippingMethodsOnCart.php index 448bdcbb37a6..f2e1be3bb050 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingMethod/SetShippingMethodsOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingMethod/SetShippingMethodsOnCart.php @@ -7,44 +7,63 @@ namespace Magento\QuoteGraphQl\Model\Resolver\ShippingMethod; +use Magento\Framework\Exception\CouldNotSaveException; +use Magento\Framework\Exception\InputException; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Exception\StateException; +use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Framework\Stdlib\ArrayManager; -use Magento\Quote\Api\CartRepositoryInterface; -use Magento\Quote\Api\Data\CartInterface; use Magento\Quote\Model\MaskedQuoteIdToQuoteIdInterface; +use Magento\Quote\Model\ShippingMethodManagementInterface; +use Magento\QuoteGraphQl\Model\Authorization\IsCartMutationAllowedForCurrentUser; +/** + * Class SetShippingMethodsOnCart + * + * Mutation resolver for setting shipping methods for shopping cart + */ class SetShippingMethodsOnCart implements ResolverInterface { - /** - * @var CartRepositoryInterface - */ - private $cartRepository; - /** * @var MaskedQuoteIdToQuoteIdInterface */ private $maskedQuoteIdToQuoteId; - /** * @var ArrayManager */ private $arrayManager; + /** + * @var ShippingMethodManagementInterface + */ + private $shippingMethodManagement; + + /** + * @var IsCartMutationAllowedForCurrentUser + */ + private $isCartMutationAllowedForCurrentUser; + /** * SetShippingMethodsOnCart constructor. * @param ArrayManager $arrayManager * @param MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId + * @param ShippingMethodManagementInterface $shippingMethodManagement */ public function __construct( ArrayManager $arrayManager, MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId, - CartRepositoryInterface $cartRepository + ShippingMethodManagementInterface $shippingMethodManagement, + IsCartMutationAllowedForCurrentUser $isCartMutationAllowedForCurrentUser ) { $this->arrayManager = $arrayManager; $this->maskedQuoteIdToQuoteId = $maskedQuoteIdToQuoteId; - $this->cartRepository = $cartRepository; + $this->shippingMethodManagement = $shippingMethodManagement; + $this->isCartMutationAllowedForCurrentUser = $isCartMutationAllowedForCurrentUser; } /** @@ -56,50 +75,56 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value $maskedCartId = $this->arrayManager->get('input/cart_id', $args); if (!$maskedCartId) { - // TODO: throw an exception + throw new GraphQlInputException(__('Required parameter "cart_id" is missing')); } if (!$shippingMethods) { - // TODO: throw an exception? + throw new GraphQlInputException(__('Required parameter "shipping_methods" is missing')); } - foreach ($shippingMethods as $shippingMethod) { - if (empty($shippingMethod['cart_address_id'])) { - // TODO: throw input exception - } - - if (empty($shippingMethod['shipping_method_code'])) { - // TODO: throw input exception - } + $shippingMethod = reset($shippingMethods); // TODO: provide implementation for multishipping - // TODO: move to a separate class - // TODO: check current customer can apply operations on specified cart + if (!$shippingMethod['shipping_carrier_code']) { // FIXME: check the E_WARNING here + throw new GraphQlInputException(__('Required parameter "shipping_carrier_code" is missing')); } - $quoteId = $this->maskedQuoteIdToQuoteId->execute($maskedCartId); - $quote = $this->cartRepository->get($quoteId); // TODO: catch no such entity exception - $this->setShippingMethods($shippingMethods, $quote); + if (!$shippingMethod['shipping_method_code']) { // FIXME: check the E_WARNING here + throw new GraphQlInputException(__('Required parameter "shipping_method_code" is missing')); + } - $quote->collectTotals(); - $quote->save(); - //$this->cartRepository->save($quote); + try { + $cartId = $this->maskedQuoteIdToQuoteId->execute((string) $maskedCartId); + } catch (NoSuchEntityException $exception) { + throw new GraphQlNoSuchEntityException( + __('Could not find a cart with ID "%masked_cart_id"', ['masked_cart_id' => $maskedCartId]) + ); + } - return 'Success!'; - } + if (false === $this->isCartMutationAllowedForCurrentUser->execute($cartId)) { + throw new GraphQlAuthorizationException( + __( + 'The current user cannot perform operations on cart "%masked_cart_id"', + ['masked_cart_id' => $maskedCartId] + ) + ); + } - private function setShippingMethods($shippingMethods, CartInterface $quote) - { - $addresses = $quote->getAllShippingAddresses(); - /** @var \Magento\Quote\Model\Quote\Address $address */ - foreach ($addresses as $address) { - $addressId = $address->getId(); - $shippingMethodForAddress = array_search($addressId, array_column($shippingMethods, 'cart_address_id')); - if ($shippingMethodForAddress !== false) { - $address->setShippingMethod($shippingMethods[$shippingMethodForAddress]['shipping_method_code']); -// $address->setCollectShippingRates(1); - $address->save(); - } + try { + $this->shippingMethodManagement->set( + $cartId, + $shippingMethods['shipping_carrier_code'], + $shippingMethods['shipping_method_code'] + ); + } catch (InputException $exception) { + throw new GraphQlInputException(__($exception->getMessage())); + } catch (CouldNotSaveException $exception) { + throw new GraphQlInputException(__($exception->getMessage())); + } catch (StateException $exception) { + throw new GraphQlInputException(__($exception->getMessage())); + } catch (NoSuchEntityException $exception) { + throw new GraphQlNoSuchEntityException(__($exception->getMessage())); } - // TODO: make sure that shipping method is assigned for all addresses + + return 'Success!'; // TODO we should return cart here } } \ No newline at end of file diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index 8ecb340db691..45f98daeaed0 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -53,7 +53,8 @@ input SetShippingMethodsOnCartInput { } input ShippingMethodForAddressInput { - cart_address_id: String! # todo: int? + cart_address_id: int + shipping_carrier_code: String! shipping_method_code: String! } @@ -66,7 +67,7 @@ type SetShippingAddressesOnCartOutput { } type SetShippingMethodsOnCartOutput { - cart: String + cart: String #TODO: temp placeholder, should be Cart! } # If no address is provided, the system get address assigned to a quote From 87cfb718edcde6b21375e398dbc8fd35cb84dcbe Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza Date: Sat, 6 Oct 2018 12:40:15 +0200 Subject: [PATCH 04/12] Working concept for setting shipping address for a shopping cart --- .../SetShippingMethodsOnCart.php | 81 +++++++++++++------ .../Magento/QuoteGraphQl/etc/schema.graphqls | 8 +- 2 files changed, 61 insertions(+), 28 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingMethod/SetShippingMethodsOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingMethod/SetShippingMethodsOnCart.php index f2e1be3bb050..c2d14668a2e7 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingMethod/SetShippingMethodsOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingMethod/SetShippingMethodsOnCart.php @@ -7,7 +7,8 @@ namespace Magento\QuoteGraphQl\Model\Resolver\ShippingMethod; -use Magento\Framework\Exception\CouldNotSaveException; +use Magento\Checkout\Api\ShippingInformationManagementInterface; +use Magento\Checkout\Model\ShippingInformation; use Magento\Framework\Exception\InputException; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Exception\StateException; @@ -19,8 +20,10 @@ use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Framework\Stdlib\ArrayManager; use Magento\Quote\Model\MaskedQuoteIdToQuoteIdInterface; -use Magento\Quote\Model\ShippingMethodManagementInterface; use Magento\QuoteGraphQl\Model\Authorization\IsCartMutationAllowedForCurrentUser; +use Magento\Quote\Model\Quote\AddressFactory as QuoteAddressFactory; +use Magento\Quote\Model\ResourceModel\Quote\Address as QuoteAddressResource; +use Magento\Checkout\Model\ShippingInformationFactory; /** * Class SetShippingMethodsOnCart @@ -29,41 +32,64 @@ */ class SetShippingMethodsOnCart implements ResolverInterface { + /** + * @var ShippingInformationFactory + */ + private $shippingInformationFactory; + + /** + * @var QuoteAddressFactory + */ + private $quoteAddressFactory; + + /** + * @var QuoteAddressResource + */ + private $quoteAddressResource; + /** * @var MaskedQuoteIdToQuoteIdInterface */ private $maskedQuoteIdToQuoteId; + /** * @var ArrayManager */ private $arrayManager; /** - * @var ShippingMethodManagementInterface + * @var IsCartMutationAllowedForCurrentUser */ - private $shippingMethodManagement; + private $isCartMutationAllowedForCurrentUser; /** - * @var IsCartMutationAllowedForCurrentUser + * @var ShippingInformationManagementInterface */ - private $isCartMutationAllowedForCurrentUser; + private $shippingInformationManagement; /** * SetShippingMethodsOnCart constructor. * @param ArrayManager $arrayManager * @param MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId - * @param ShippingMethodManagementInterface $shippingMethodManagement + * @param IsCartMutationAllowedForCurrentUser $isCartMutationAllowedForCurrentUser */ public function __construct( ArrayManager $arrayManager, MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId, - ShippingMethodManagementInterface $shippingMethodManagement, - IsCartMutationAllowedForCurrentUser $isCartMutationAllowedForCurrentUser + IsCartMutationAllowedForCurrentUser $isCartMutationAllowedForCurrentUser, + ShippingInformationManagementInterface $shippingInformationManagement, + QuoteAddressFactory $quoteAddressFacrory, + QuoteAddressResource $quoteAddressResource, + ShippingInformationFactory $shippingInformationFactory ) { $this->arrayManager = $arrayManager; $this->maskedQuoteIdToQuoteId = $maskedQuoteIdToQuoteId; - $this->shippingMethodManagement = $shippingMethodManagement; $this->isCartMutationAllowedForCurrentUser = $isCartMutationAllowedForCurrentUser; + $this->shippingInformationManagement = $shippingInformationManagement; + + $this->quoteAddressResource = $quoteAddressResource; + $this->quoteAddressFactory = $quoteAddressFacrory; + $this->shippingInformationFactory = $shippingInformationFactory; } /** @@ -77,17 +103,18 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value if (!$maskedCartId) { throw new GraphQlInputException(__('Required parameter "cart_id" is missing')); } - if (!$shippingMethods) { throw new GraphQlInputException(__('Required parameter "shipping_methods" is missing')); } $shippingMethod = reset($shippingMethods); // TODO: provide implementation for multishipping + if (!$shippingMethod['cart_address_id']) { + throw new GraphQlInputException(__('Required parameter "cart_address_id" is missing')); + } if (!$shippingMethod['shipping_carrier_code']) { // FIXME: check the E_WARNING here throw new GraphQlInputException(__('Required parameter "shipping_carrier_code" is missing')); } - if (!$shippingMethod['shipping_method_code']) { // FIXME: check the E_WARNING here throw new GraphQlInputException(__('Required parameter "shipping_method_code" is missing')); } @@ -109,22 +136,28 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value ); } + $quoteAddress = $this->quoteAddressFactory->create(); + $this->quoteAddressResource->load($quoteAddress, $shippingMethod['cart_address_id']); + + /** @var ShippingInformation $shippingInformation */ + $shippingInformation = $this->shippingInformationFactory->create(); + + /* If the address is not a shipping address (but billing) the system will find the proper shipping address for + the selected cart and set the information there (actual for single shipping address) */ + $shippingInformation->setShippingAddress($quoteAddress); + $shippingInformation->setShippingCarrierCode($shippingMethod['shipping_carrier_code']); + $shippingInformation->setShippingMethodCode($shippingMethod['shipping_method_code']); + try { - $this->shippingMethodManagement->set( - $cartId, - $shippingMethods['shipping_carrier_code'], - $shippingMethods['shipping_method_code'] - ); - } catch (InputException $exception) { - throw new GraphQlInputException(__($exception->getMessage())); - } catch (CouldNotSaveException $exception) { - throw new GraphQlInputException(__($exception->getMessage())); - } catch (StateException $exception) { - throw new GraphQlInputException(__($exception->getMessage())); + $this->shippingInformationManagement->saveAddressInformation($cartId, $shippingInformation); } catch (NoSuchEntityException $exception) { throw new GraphQlNoSuchEntityException(__($exception->getMessage())); + } catch (StateException $exception) { + throw new GraphQlInputException(__($exception->getMessage())); + } catch (InputException $exception) { + throw new GraphQlInputException(__($exception->getMessage())); } return 'Success!'; // TODO we should return cart here } -} \ No newline at end of file +} diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index 45f98daeaed0..730a54377b37 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -6,9 +6,9 @@ type Query { } type Mutation { - createEmptyCart: String @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\Cart\\CreateEmptyCart") @doc(description:"Creates empty shopping cart for guest or logged in user") - applyCouponToCart(input: ApplyCouponToCartInput): ApplyCouponToCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\Coupon\\ApplyCouponToCart") - removeCouponFromCart(input: RemoveCouponFromCartInput): RemoveCouponFromCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\Coupon\\RemoveCouponFromCart") + createEmptyCart: String @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\CreateEmptyCart") @doc(description:"Creates empty shopping cart for guest or logged in user") + applyCouponToCart(input: ApplyCouponToCartInput): ApplyCouponToCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\ApplyCouponToCart") + removeCouponFromCart(input: RemoveCouponFromCartInput): RemoveCouponFromCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\RemoveCouponFromCart") setShippingAddressesOnCart(input: SetShippingAddressesOnCartInput): SetShippingAddressesOnCartOutput setBillingAddressOnCart(input: SetBillingAddressOnCartInput): SetBillingAddressOnCartOutput setShippingMethodsOnCart(input: SetShippingMethodsOnCartInput): SetShippingMethodsOnCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\ShippingMethod\\SetShippingMethodsOnCart") @@ -53,7 +53,7 @@ input SetShippingMethodsOnCartInput { } input ShippingMethodForAddressInput { - cart_address_id: int + cart_address_id: Int! shipping_carrier_code: String! shipping_method_code: String! } From 97b519cafff9638d9455d9dc2e0aaca3704d805b Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza Date: Mon, 22 Oct 2018 19:19:21 +0200 Subject: [PATCH 05/12] Introduced cart address resolver and data provider --- .../Resolver/Address/AddressDataProvider.php | 70 +++++++++++++++ .../Model/Resolver/CartAddress.php | 87 +++++++++++++++++++ .../SetShippingMethodsOnCart.php | 6 +- .../Magento/QuoteGraphQl/etc/schema.graphqls | 22 ++--- 4 files changed, 173 insertions(+), 12 deletions(-) create mode 100644 app/code/Magento/QuoteGraphQl/Model/Resolver/Address/AddressDataProvider.php create mode 100644 app/code/Magento/QuoteGraphQl/Model/Resolver/CartAddress.php diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/Address/AddressDataProvider.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/Address/AddressDataProvider.php new file mode 100644 index 000000000000..d401f296cb46 --- /dev/null +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/Address/AddressDataProvider.php @@ -0,0 +1,70 @@ +dataObjectConverter = $dataObjectConverter; + } + + /** + * Collect and return information about shipping and billing addresses + * + * @param CartInterface $cart + * @return array + */ + public function getCartAddresses(CartInterface $cart): array + { + $addressData = []; + $shippingAddress = $cart->getShippingAddress(); + $billingAddress = $cart->getBillingAddress(); + + if ($shippingAddress) { + $shippingData = $this->dataObjectConverter->toFlatArray($shippingAddress, [], AddressInterface::class); + $shippingData['address_type'] = 'SHIPPING'; + $shippingData['selected_shipping_method'] = [ + 'code' => $shippingAddress->getShippingMethod(), + 'label' => $shippingAddress->getShippingDescription(), + 'free_shipping' => $shippingAddress->getFreeShipping(), + ]; + $shippingData['items_weight'] = $shippingAddress->getWeight(); + $shippingData['customer_notes'] = $shippingAddress->getCustomerNotes(); + $addressData[] = $shippingData; + } + + if ($billingAddress) { + $billingData = $this->dataObjectConverter->toFlatArray($billingAddress, [], AddressInterface::class); + $billingData['address_type'] = 'BILLING'; + $addressData[] = $billingData; + } + + return $addressData; + } +} diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/CartAddress.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/CartAddress.php new file mode 100644 index 000000000000..f83a112ba5ba --- /dev/null +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/CartAddress.php @@ -0,0 +1,87 @@ +maskedQuoteIdToQuoteId = $maskedQuoteIdToQuoteId; + $this->cartRepository = $cartRepository; + $this->addressDataProvider = $addressDataProvider; + } + + /** + * @inheritdoc + */ + public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) + { + if (!isset($value['cart_id'])) { + // TODO: consider the possibility to pass quote model instead od quote ID + throw new LocalizedException(__('"cart_id" value should be specified')); + } + + try { + $quoteId = $this->maskedQuoteIdToQuoteId->execute($value['cart_id']); + } catch (NoSuchEntityException $exception) { + throw new GraphQlNoSuchEntityException( + __('Could not find a cart with ID "%masked_cart_id"', ['masked_cart_id' => $value['cart_id']]) + ); + } + + // TODO: should we check customer permissions here as well? + + try { + $quote = $this->cartRepository->get($quoteId); + } catch (NoSuchEntityException $exception) { + throw new GraphQlNoSuchEntityException( + __('Could not find a cart with ID "%quote_id"', ['quote_id' => $quoteId]) + ); + } + + return $this->addressDataProvider->getCartAddresses($quote); + } +} diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingMethod/SetShippingMethodsOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingMethod/SetShippingMethodsOnCart.php index c2d14668a2e7..1f35f37d9cf2 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingMethod/SetShippingMethodsOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingMethod/SetShippingMethodsOnCart.php @@ -158,6 +158,10 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value throw new GraphQlInputException(__($exception->getMessage())); } - return 'Success!'; // TODO we should return cart here + return [ + 'cart' => [ + 'cart_id' => $maskedCartId + ] + ]; } } diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index 730a54377b37..cbc56bfaea66 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -67,7 +67,7 @@ type SetShippingAddressesOnCartOutput { } type SetShippingMethodsOnCartOutput { - cart: String #TODO: temp placeholder, should be Cart! + cart: Cart! } # If no address is provided, the system get address assigned to a quote @@ -92,28 +92,28 @@ type ApplyCouponToCartOutput { } type Cart { + cart_id: String items: [CartItemInterface] applied_coupon: AppliedCoupon - addresses: [CartAddress]! + addresses: [CartAddress]! @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\CartAddress") } type CartAddress { - firstname: String! - lastname: String! + firstname: String + lastname: String company: String - street: [String!]! - city: String! + street: [String] + city: String region: CartAddressRegion postcode: String - country: CartAddressCountry! - telephone: String! - address_type: AdressTypeEnum! + country: CartAddressCountry + telephone: String + address_type: AdressTypeEnum selected_shipping_method: CheckoutShippingMethod - available_shipping_methods: [CheckoutShippingMethod]! + available_shipping_methods: [CheckoutShippingMethod] items_weight: Float customer_notes: String cart_items: [CartItemQuantity] - applied_coupon: AppliedCoupon } type CartItemQuantity { From 42b235827941fb1713a360edcdbeebb0a95a35d4 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza Date: Tue, 30 Oct 2018 11:45:50 +0100 Subject: [PATCH 06/12] Consolidated cart address information provider --- .../Resolver/Address/AddressDataProvider.php | 42 +++++++++++++++---- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/Address/AddressDataProvider.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/Address/AddressDataProvider.php index d401f296cb46..80fe973c4e44 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/Address/AddressDataProvider.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/Address/AddressDataProvider.php @@ -10,6 +10,7 @@ use Magento\Framework\Api\ExtensibleDataObjectConverter; use Magento\Quote\Api\Data\AddressInterface; use Magento\Quote\Api\Data\CartInterface; +use Magento\Quote\Model\Quote\Address as QuoteAddress; /** * Class AddressDataProvider @@ -49,22 +50,45 @@ public function getCartAddresses(CartInterface $cart): array if ($shippingAddress) { $shippingData = $this->dataObjectConverter->toFlatArray($shippingAddress, [], AddressInterface::class); $shippingData['address_type'] = 'SHIPPING'; - $shippingData['selected_shipping_method'] = [ - 'code' => $shippingAddress->getShippingMethod(), - 'label' => $shippingAddress->getShippingDescription(), - 'free_shipping' => $shippingAddress->getFreeShipping(), - ]; - $shippingData['items_weight'] = $shippingAddress->getWeight(); - $shippingData['customer_notes'] = $shippingAddress->getCustomerNotes(); - $addressData[] = $shippingData; + $addressData[] = array_merge($shippingData, $this->extractAddressData($shippingAddress)); } if ($billingAddress) { $billingData = $this->dataObjectConverter->toFlatArray($billingAddress, [], AddressInterface::class); $billingData['address_type'] = 'BILLING'; - $addressData[] = $billingData; + $addressData[] = array_merge($billingData, $this->extractAddressData($billingAddress)); } return $addressData; } + + /** + * Extract the necessary address fields from address model + * + * @param QuoteAddress $address + * @return array + */ + private function extractAddressData(QuoteAddress $address): array + { + $addressData = [ + 'country' => [ + 'code' => $address->getCountryId(), + 'label' => $address->getCountry() + ], + 'region' => [ + 'code' => $address->getRegionCode(), + 'label' => $address->getRegion() + ], + 'street' => $address->getStreet(), + 'selected_shipping_method' => [ + 'code' => $address->getShippingMethod(), + 'label' => $address->getShippingDescription(), + 'free_shipping' => $address->getFreeShipping(), + ], + 'items_weight' => $address->getWeight(), + 'customer_notes' => $address->getCustomerNotes() + ]; + + return $addressData; + } } From 0cf59de0e706b213e836949700a713fdfa9cf4f4 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza Date: Fri, 2 Nov 2018 11:33:02 +0200 Subject: [PATCH 07/12] Added user permission check --- .../Model/Resolver/CartAddress.php | 30 +++++++++++++++---- .../SetShippingMethodsOnCart.php | 8 +++-- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/CartAddress.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/CartAddress.php index f83a112ba5ba..d2b502688adb 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/CartAddress.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/CartAddress.php @@ -10,11 +10,13 @@ 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\GraphQlNoSuchEntityException; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Quote\Api\CartRepositoryInterface; use Magento\Quote\Model\MaskedQuoteIdToQuoteId; +use Magento\QuoteGraphQl\Model\Authorization\IsCartMutationAllowedForCurrentUser; use Magento\QuoteGraphQl\Model\Resolver\Address\AddressDataProvider; /** @@ -27,6 +29,11 @@ class CartAddress implements ResolverInterface */ private $addressDataProvider; + /** + * @var IsCartMutationAllowedForCurrentUser + */ + private $isCartMutationAllowedForCurrentUser; + /** * @var CartRepositoryInterface */ @@ -43,15 +50,18 @@ class CartAddress implements ResolverInterface * @param MaskedQuoteIdToQuoteId $maskedQuoteIdToQuoteId * @param CartRepositoryInterface $cartRepository * @param AddressDataProvider $addressDataProvider + * @param IsCartMutationAllowedForCurrentUser $isCartMutationAllowedForCurrentUser */ public function __construct( MaskedQuoteIdToQuoteId $maskedQuoteIdToQuoteId, CartRepositoryInterface $cartRepository, - AddressDataProvider $addressDataProvider + AddressDataProvider $addressDataProvider, + IsCartMutationAllowedForCurrentUser $isCartMutationAllowedForCurrentUser ) { $this->maskedQuoteIdToQuoteId = $maskedQuoteIdToQuoteId; $this->cartRepository = $cartRepository; $this->addressDataProvider = $addressDataProvider; + $this->isCartMutationAllowedForCurrentUser = $isCartMutationAllowedForCurrentUser; } /** @@ -59,20 +69,30 @@ public function __construct( */ public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) { + /* The cart_id is used instead of the model because some parent resolvers do not work + with cart model */ if (!isset($value['cart_id'])) { - // TODO: consider the possibility to pass quote model instead od quote ID throw new LocalizedException(__('"cart_id" value should be specified')); } + $maskedCartId = $value['cart_id']; + try { - $quoteId = $this->maskedQuoteIdToQuoteId->execute($value['cart_id']); + $quoteId = $this->maskedQuoteIdToQuoteId->execute($maskedCartId); } catch (NoSuchEntityException $exception) { throw new GraphQlNoSuchEntityException( - __('Could not find a cart with ID "%masked_cart_id"', ['masked_cart_id' => $value['cart_id']]) + __('Could not find a cart with ID "%masked_cart_id"', ['masked_cart_id' => $maskedCartId]) ); } - // TODO: should we check customer permissions here as well? + if (false === $this->isCartMutationAllowedForCurrentUser->execute($quoteId)) { + throw new GraphQlAuthorizationException( + __( + 'The current user cannot perform operations on cart "%masked_cart_id"', + ['masked_cart_id' => $maskedCartId] + ) + ); + } try { $quote = $this->cartRepository->get($quoteId); diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingMethod/SetShippingMethodsOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingMethod/SetShippingMethodsOnCart.php index 1f35f37d9cf2..7e35f97d9f71 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingMethod/SetShippingMethodsOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingMethod/SetShippingMethodsOnCart.php @@ -72,13 +72,17 @@ class SetShippingMethodsOnCart implements ResolverInterface * @param ArrayManager $arrayManager * @param MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId * @param IsCartMutationAllowedForCurrentUser $isCartMutationAllowedForCurrentUser + * @param ShippingInformationManagementInterface $shippingInformationManagement + * @param QuoteAddressFactory $quoteAddressFactory + * @param QuoteAddressResource $quoteAddressResource + * @param ShippingInformationFactory $shippingInformationFactory */ public function __construct( ArrayManager $arrayManager, MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId, IsCartMutationAllowedForCurrentUser $isCartMutationAllowedForCurrentUser, ShippingInformationManagementInterface $shippingInformationManagement, - QuoteAddressFactory $quoteAddressFacrory, + QuoteAddressFactory $quoteAddressFactory, QuoteAddressResource $quoteAddressResource, ShippingInformationFactory $shippingInformationFactory ) { @@ -88,7 +92,7 @@ public function __construct( $this->shippingInformationManagement = $shippingInformationManagement; $this->quoteAddressResource = $quoteAddressResource; - $this->quoteAddressFactory = $quoteAddressFacrory; + $this->quoteAddressFactory = $quoteAddressFactory; $this->shippingInformationFactory = $shippingInformationFactory; } From 5c0bcfd2160d549c082d7f51df0562ab674a9fe6 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza Date: Fri, 2 Nov 2018 11:58:47 +0200 Subject: [PATCH 08/12] Refactored flow for new authorization check logic --- .../Model/Resolver/CartAddress.php | 49 ++--------------- .../SetShippingMethodsOnCart.php | 52 +++++-------------- 2 files changed, 19 insertions(+), 82 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/CartAddress.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/CartAddress.php index d2b502688adb..54bd8fa2a571 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/CartAddress.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/CartAddress.php @@ -8,15 +8,11 @@ namespace Magento\QuoteGraphQl\Model\Resolver; 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\GraphQlNoSuchEntityException; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Quote\Api\CartRepositoryInterface; use Magento\Quote\Model\MaskedQuoteIdToQuoteId; -use Magento\QuoteGraphQl\Model\Authorization\IsCartMutationAllowedForCurrentUser; use Magento\QuoteGraphQl\Model\Resolver\Address\AddressDataProvider; /** @@ -29,11 +25,6 @@ class CartAddress implements ResolverInterface */ private $addressDataProvider; - /** - * @var IsCartMutationAllowedForCurrentUser - */ - private $isCartMutationAllowedForCurrentUser; - /** * @var CartRepositoryInterface */ @@ -50,18 +41,15 @@ class CartAddress implements ResolverInterface * @param MaskedQuoteIdToQuoteId $maskedQuoteIdToQuoteId * @param CartRepositoryInterface $cartRepository * @param AddressDataProvider $addressDataProvider - * @param IsCartMutationAllowedForCurrentUser $isCartMutationAllowedForCurrentUser */ public function __construct( MaskedQuoteIdToQuoteId $maskedQuoteIdToQuoteId, CartRepositoryInterface $cartRepository, - AddressDataProvider $addressDataProvider, - IsCartMutationAllowedForCurrentUser $isCartMutationAllowedForCurrentUser + AddressDataProvider $addressDataProvider ) { $this->maskedQuoteIdToQuoteId = $maskedQuoteIdToQuoteId; $this->cartRepository = $cartRepository; $this->addressDataProvider = $addressDataProvider; - $this->isCartMutationAllowedForCurrentUser = $isCartMutationAllowedForCurrentUser; } /** @@ -69,39 +57,12 @@ public function __construct( */ public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) { - /* The cart_id is used instead of the model because some parent resolvers do not work - with cart model */ - if (!isset($value['cart_id'])) { - throw new LocalizedException(__('"cart_id" value should be specified')); + if (!isset($value['model'])) { + throw new LocalizedException(__('"model" value should be specified')); } - $maskedCartId = $value['cart_id']; - - try { - $quoteId = $this->maskedQuoteIdToQuoteId->execute($maskedCartId); - } catch (NoSuchEntityException $exception) { - throw new GraphQlNoSuchEntityException( - __('Could not find a cart with ID "%masked_cart_id"', ['masked_cart_id' => $maskedCartId]) - ); - } - - if (false === $this->isCartMutationAllowedForCurrentUser->execute($quoteId)) { - throw new GraphQlAuthorizationException( - __( - 'The current user cannot perform operations on cart "%masked_cart_id"', - ['masked_cart_id' => $maskedCartId] - ) - ); - } - - try { - $quote = $this->cartRepository->get($quoteId); - } catch (NoSuchEntityException $exception) { - throw new GraphQlNoSuchEntityException( - __('Could not find a cart with ID "%quote_id"', ['quote_id' => $quoteId]) - ); - } + $cart = $value['model']; - return $this->addressDataProvider->getCartAddresses($quote); + return $this->addressDataProvider->getCartAddresses($cart); } } diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingMethod/SetShippingMethodsOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingMethod/SetShippingMethodsOnCart.php index 7e35f97d9f71..97a7bcea8aa4 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingMethod/SetShippingMethodsOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingMethod/SetShippingMethodsOnCart.php @@ -12,18 +12,16 @@ use Magento\Framework\Exception\InputException; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Exception\StateException; -use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Framework\Stdlib\ArrayManager; -use Magento\Quote\Model\MaskedQuoteIdToQuoteIdInterface; -use Magento\QuoteGraphQl\Model\Authorization\IsCartMutationAllowedForCurrentUser; use Magento\Quote\Model\Quote\AddressFactory as QuoteAddressFactory; use Magento\Quote\Model\ResourceModel\Quote\Address as QuoteAddressResource; use Magento\Checkout\Model\ShippingInformationFactory; +use Magento\QuoteGraphQl\Model\Cart\GetCartForUser; /** * Class SetShippingMethodsOnCart @@ -47,20 +45,15 @@ class SetShippingMethodsOnCart implements ResolverInterface */ private $quoteAddressResource; - /** - * @var MaskedQuoteIdToQuoteIdInterface - */ - private $maskedQuoteIdToQuoteId; - /** * @var ArrayManager */ private $arrayManager; /** - * @var IsCartMutationAllowedForCurrentUser + * @var GetCartForUser */ - private $isCartMutationAllowedForCurrentUser; + private $getCartForUser; /** * @var ShippingInformationManagementInterface @@ -70,8 +63,7 @@ class SetShippingMethodsOnCart implements ResolverInterface /** * SetShippingMethodsOnCart constructor. * @param ArrayManager $arrayManager - * @param MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId - * @param IsCartMutationAllowedForCurrentUser $isCartMutationAllowedForCurrentUser + * @param GetCartForUser $getCartForUser * @param ShippingInformationManagementInterface $shippingInformationManagement * @param QuoteAddressFactory $quoteAddressFactory * @param QuoteAddressResource $quoteAddressResource @@ -79,18 +71,15 @@ class SetShippingMethodsOnCart implements ResolverInterface */ public function __construct( ArrayManager $arrayManager, - MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId, - IsCartMutationAllowedForCurrentUser $isCartMutationAllowedForCurrentUser, + GetCartForUser $getCartForUser, ShippingInformationManagementInterface $shippingInformationManagement, QuoteAddressFactory $quoteAddressFactory, QuoteAddressResource $quoteAddressResource, ShippingInformationFactory $shippingInformationFactory ) { $this->arrayManager = $arrayManager; - $this->maskedQuoteIdToQuoteId = $maskedQuoteIdToQuoteId; - $this->isCartMutationAllowedForCurrentUser = $isCartMutationAllowedForCurrentUser; + $this->getCartForUser = $getCartForUser; $this->shippingInformationManagement = $shippingInformationManagement; - $this->quoteAddressResource = $quoteAddressResource; $this->quoteAddressFactory = $quoteAddressFactory; $this->shippingInformationFactory = $shippingInformationFactory; @@ -111,34 +100,20 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value throw new GraphQlInputException(__('Required parameter "shipping_methods" is missing')); } - $shippingMethod = reset($shippingMethods); // TODO: provide implementation for multishipping + $shippingMethod = reset($shippingMethods); if (!$shippingMethod['cart_address_id']) { throw new GraphQlInputException(__('Required parameter "cart_address_id" is missing')); } - if (!$shippingMethod['shipping_carrier_code']) { // FIXME: check the E_WARNING here + if (!$shippingMethod['shipping_carrier_code']) { throw new GraphQlInputException(__('Required parameter "shipping_carrier_code" is missing')); } - if (!$shippingMethod['shipping_method_code']) { // FIXME: check the E_WARNING here + if (!$shippingMethod['shipping_method_code']) { throw new GraphQlInputException(__('Required parameter "shipping_method_code" is missing')); } - try { - $cartId = $this->maskedQuoteIdToQuoteId->execute((string) $maskedCartId); - } catch (NoSuchEntityException $exception) { - throw new GraphQlNoSuchEntityException( - __('Could not find a cart with ID "%masked_cart_id"', ['masked_cart_id' => $maskedCartId]) - ); - } - - if (false === $this->isCartMutationAllowedForCurrentUser->execute($cartId)) { - throw new GraphQlAuthorizationException( - __( - 'The current user cannot perform operations on cart "%masked_cart_id"', - ['masked_cart_id' => $maskedCartId] - ) - ); - } + $userId = $context->getUserId(); + $cart = $this->getCartForUser->execute((string) $maskedCartId, $userId); $quoteAddress = $this->quoteAddressFactory->create(); $this->quoteAddressResource->load($quoteAddress, $shippingMethod['cart_address_id']); @@ -153,7 +128,7 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value $shippingInformation->setShippingMethodCode($shippingMethod['shipping_method_code']); try { - $this->shippingInformationManagement->saveAddressInformation($cartId, $shippingInformation); + $this->shippingInformationManagement->saveAddressInformation($cart->getId(), $shippingInformation); } catch (NoSuchEntityException $exception) { throw new GraphQlNoSuchEntityException(__($exception->getMessage())); } catch (StateException $exception) { @@ -164,7 +139,8 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value return [ 'cart' => [ - 'cart_id' => $maskedCartId + 'cart_id' => $maskedCartId, + 'model' => $cart ] ]; } From c98c3d5142d73789e8233deb4aa90f172d716c21 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza Date: Fri, 2 Nov 2018 12:29:44 +0200 Subject: [PATCH 09/12] Logic for setting shipping method is moved to a separate class --- .../SetShippingMethodOnCart.php} | 84 ++++------------ .../Resolver/SetShippingMethodsOnCart.php | 99 +++++++++++++++++++ .../Magento/QuoteGraphQl/etc/schema.graphqls | 2 +- 3 files changed, 119 insertions(+), 66 deletions(-) rename app/code/Magento/QuoteGraphQl/Model/{Resolver/ShippingMethod/SetShippingMethodsOnCart.php => Cart/SetShippingMethodOnCart.php} (54%) create mode 100644 app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingMethodsOnCart.php diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingMethod/SetShippingMethodsOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingMethodOnCart.php similarity index 54% rename from app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingMethod/SetShippingMethodsOnCart.php rename to app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingMethodOnCart.php index 97a7bcea8aa4..7f945dca2fd7 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingMethod/SetShippingMethodsOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingMethodOnCart.php @@ -5,30 +5,26 @@ */ declare(strict_types=1); -namespace Magento\QuoteGraphQl\Model\Resolver\ShippingMethod; +namespace Magento\QuoteGraphQl\Model\Cart; -use Magento\Checkout\Api\ShippingInformationManagementInterface; -use Magento\Checkout\Model\ShippingInformation; use Magento\Framework\Exception\InputException; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Exception\StateException; use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; -use Magento\Framework\GraphQl\Query\ResolverInterface; -use Magento\Framework\GraphQl\Config\Element\Field; -use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; -use Magento\Framework\Stdlib\ArrayManager; +use Magento\Quote\Model\Quote; use Magento\Quote\Model\Quote\AddressFactory as QuoteAddressFactory; use Magento\Quote\Model\ResourceModel\Quote\Address as QuoteAddressResource; use Magento\Checkout\Model\ShippingInformationFactory; -use Magento\QuoteGraphQl\Model\Cart\GetCartForUser; +use Magento\Checkout\Api\ShippingInformationManagementInterface; +use Magento\Checkout\Model\ShippingInformation; /** * Class SetShippingMethodsOnCart * - * Mutation resolver for setting shipping methods for shopping cart + * Set shipping method for a specified shopping cart address */ -class SetShippingMethodsOnCart implements ResolverInterface +class SetShippingMethodOnCart { /** * @var ShippingInformationFactory @@ -45,40 +41,23 @@ class SetShippingMethodsOnCart implements ResolverInterface */ private $quoteAddressResource; - /** - * @var ArrayManager - */ - private $arrayManager; - - /** - * @var GetCartForUser - */ - private $getCartForUser; - /** * @var ShippingInformationManagementInterface */ private $shippingInformationManagement; /** - * SetShippingMethodsOnCart constructor. - * @param ArrayManager $arrayManager - * @param GetCartForUser $getCartForUser * @param ShippingInformationManagementInterface $shippingInformationManagement * @param QuoteAddressFactory $quoteAddressFactory * @param QuoteAddressResource $quoteAddressResource * @param ShippingInformationFactory $shippingInformationFactory */ public function __construct( - ArrayManager $arrayManager, - GetCartForUser $getCartForUser, ShippingInformationManagementInterface $shippingInformationManagement, QuoteAddressFactory $quoteAddressFactory, QuoteAddressResource $quoteAddressResource, ShippingInformationFactory $shippingInformationFactory ) { - $this->arrayManager = $arrayManager; - $this->getCartForUser = $getCartForUser; $this->shippingInformationManagement = $shippingInformationManagement; $this->quoteAddressResource = $quoteAddressResource; $this->quoteAddressFactory = $quoteAddressFactory; @@ -86,37 +65,19 @@ public function __construct( } /** - * @inheritdoc + * Sets shipping method for a specified shopping cart address + * + * @param Quote $cart + * @param int $cartAddressId + * @param string $carrierCode + * @param string $methodCode + * @throws GraphQlInputException + * @throws GraphQlNoSuchEntityException */ - public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) + public function execute(Quote $cart, int $cartAddressId, string $carrierCode, string $methodCode): void { - $shippingMethods = $this->arrayManager->get('input/shipping_methods', $args); - $maskedCartId = $this->arrayManager->get('input/cart_id', $args); - - if (!$maskedCartId) { - throw new GraphQlInputException(__('Required parameter "cart_id" is missing')); - } - if (!$shippingMethods) { - throw new GraphQlInputException(__('Required parameter "shipping_methods" is missing')); - } - - $shippingMethod = reset($shippingMethods); - - if (!$shippingMethod['cart_address_id']) { - throw new GraphQlInputException(__('Required parameter "cart_address_id" is missing')); - } - if (!$shippingMethod['shipping_carrier_code']) { - throw new GraphQlInputException(__('Required parameter "shipping_carrier_code" is missing')); - } - if (!$shippingMethod['shipping_method_code']) { - throw new GraphQlInputException(__('Required parameter "shipping_method_code" is missing')); - } - - $userId = $context->getUserId(); - $cart = $this->getCartForUser->execute((string) $maskedCartId, $userId); - $quoteAddress = $this->quoteAddressFactory->create(); - $this->quoteAddressResource->load($quoteAddress, $shippingMethod['cart_address_id']); + $this->quoteAddressResource->load($quoteAddress, $cartAddressId); /** @var ShippingInformation $shippingInformation */ $shippingInformation = $this->shippingInformationFactory->create(); @@ -124,8 +85,8 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value /* If the address is not a shipping address (but billing) the system will find the proper shipping address for the selected cart and set the information there (actual for single shipping address) */ $shippingInformation->setShippingAddress($quoteAddress); - $shippingInformation->setShippingCarrierCode($shippingMethod['shipping_carrier_code']); - $shippingInformation->setShippingMethodCode($shippingMethod['shipping_method_code']); + $shippingInformation->setShippingCarrierCode($carrierCode); + $shippingInformation->setShippingMethodCode($methodCode); try { $this->shippingInformationManagement->saveAddressInformation($cart->getId(), $shippingInformation); @@ -136,12 +97,5 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value } catch (InputException $exception) { throw new GraphQlInputException(__($exception->getMessage())); } - - return [ - 'cart' => [ - 'cart_id' => $maskedCartId, - 'model' => $cart - ] - ]; } -} +} \ No newline at end of file diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingMethodsOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingMethodsOnCart.php new file mode 100644 index 000000000000..920829f5d67b --- /dev/null +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingMethodsOnCart.php @@ -0,0 +1,99 @@ +arrayManager = $arrayManager; + $this->getCartForUser = $getCartForUser; + $this->setShippingMethodOnCart = $setShippingMethodOnCart; + } + + /** + * @inheritdoc + */ + public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) + { + $shippingMethods = $this->arrayManager->get('input/shipping_methods', $args); + $maskedCartId = $this->arrayManager->get('input/cart_id', $args); + + if (!$maskedCartId) { + throw new GraphQlInputException(__('Required parameter "cart_id" is missing')); + } + if (!$shippingMethods) { + throw new GraphQlInputException(__('Required parameter "shipping_methods" is missing')); + } + + $shippingMethod = reset($shippingMethods); // This point can be extended for multishipping + + if (!$shippingMethod['cart_address_id']) { + throw new GraphQlInputException(__('Required parameter "cart_address_id" is missing')); + } + if (!$shippingMethod['shipping_carrier_code']) { + throw new GraphQlInputException(__('Required parameter "shipping_carrier_code" is missing')); + } + if (!$shippingMethod['shipping_method_code']) { + throw new GraphQlInputException(__('Required parameter "shipping_method_code" is missing')); + } + + $userId = $context->getUserId(); + $cart = $this->getCartForUser->execute((string) $maskedCartId, $userId); + + $this->setShippingMethodOnCart->execute( + $cart, + $shippingMethod['cart_address_id'], + $shippingMethod['shipping_carrier_code'], + $shippingMethod['shipping_method_code'] + ); + + return [ + 'cart' => [ + 'cart_id' => $maskedCartId, + 'model' => $cart + ] + ]; + } +} diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index cbc56bfaea66..ce982952f1ae 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -11,7 +11,7 @@ type Mutation { removeCouponFromCart(input: RemoveCouponFromCartInput): RemoveCouponFromCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\RemoveCouponFromCart") setShippingAddressesOnCart(input: SetShippingAddressesOnCartInput): SetShippingAddressesOnCartOutput setBillingAddressOnCart(input: SetBillingAddressOnCartInput): SetBillingAddressOnCartOutput - setShippingMethodsOnCart(input: SetShippingMethodsOnCartInput): SetShippingMethodsOnCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\ShippingMethod\\SetShippingMethodsOnCart") + setShippingMethodsOnCart(input: SetShippingMethodsOnCartInput): SetShippingMethodsOnCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\SetShippingMethodsOnCart") addSimpleProductsToCart(input: AddSimpleProductsToCartInput): AddSimpleProductsToCartOutput @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\AddSimpleProductsToCart") } From c0c1d76192b57fe524d0b9d2672282221ea8b011 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza Date: Tue, 6 Nov 2018 14:07:03 +0100 Subject: [PATCH 10/12] Added checkout module dependency --- app/code/Magento/QuoteGraphQl/composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/QuoteGraphQl/composer.json b/app/code/Magento/QuoteGraphQl/composer.json index c9900dd5f315..f0d2eea9bcaf 100644 --- a/app/code/Magento/QuoteGraphQl/composer.json +++ b/app/code/Magento/QuoteGraphQl/composer.json @@ -6,6 +6,7 @@ "php": "~7.1.3||~7.2.0", "magento/framework": "*", "magento/module-quote": "*", + "magento/module-checkout": "*", "magento/module-catalog": "*", "magento/module-store": "*" }, From 7dba16ba0d61af22b241d58d99f026d0a140222f Mon Sep 17 00:00:00 2001 From: Valeriy Nayda Date: Tue, 13 Nov 2018 15:55:47 +0200 Subject: [PATCH 11/12] GraphQl-39: Manage Shipping methods on Cart --- .../Address/AddressDataProvider.php | 2 +- .../{CartAddress.php => CartAddresses.php} | 24 ++----------------- .../Magento/QuoteGraphQl/etc/schema.graphqls | 2 +- 3 files changed, 4 insertions(+), 24 deletions(-) rename app/code/Magento/QuoteGraphQl/Model/{Resolver => Cart}/Address/AddressDataProvider.php (98%) rename app/code/Magento/QuoteGraphQl/Model/Resolver/{CartAddress.php => CartAddresses.php} (59%) diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/Address/AddressDataProvider.php b/app/code/Magento/QuoteGraphQl/Model/Cart/Address/AddressDataProvider.php similarity index 98% rename from app/code/Magento/QuoteGraphQl/Model/Resolver/Address/AddressDataProvider.php rename to app/code/Magento/QuoteGraphQl/Model/Cart/Address/AddressDataProvider.php index 80fe973c4e44..fb742477ec99 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/Address/AddressDataProvider.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/Address/AddressDataProvider.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\QuoteGraphQl\Model\Resolver\Address; +namespace Magento\QuoteGraphQl\Model\Cart\Address; use Magento\Framework\Api\ExtensibleDataObjectConverter; use Magento\Quote\Api\Data\AddressInterface; diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/CartAddress.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/CartAddresses.php similarity index 59% rename from app/code/Magento/QuoteGraphQl/Model/Resolver/CartAddress.php rename to app/code/Magento/QuoteGraphQl/Model/Resolver/CartAddresses.php index 54bd8fa2a571..69544672bf12 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/CartAddress.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/CartAddresses.php @@ -11,14 +11,12 @@ use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; -use Magento\Quote\Api\CartRepositoryInterface; -use Magento\Quote\Model\MaskedQuoteIdToQuoteId; -use Magento\QuoteGraphQl\Model\Resolver\Address\AddressDataProvider; +use Magento\QuoteGraphQl\Model\Cart\Address\AddressDataProvider; /** * @inheritdoc */ -class CartAddress implements ResolverInterface +class CartAddresses implements ResolverInterface { /** * @var AddressDataProvider @@ -26,29 +24,11 @@ class CartAddress implements ResolverInterface private $addressDataProvider; /** - * @var CartRepositoryInterface - */ - private $cartRepository; - - /** - * @var MaskedQuoteIdToQuoteId - */ - private $maskedQuoteIdToQuoteId; - - /** - * CartAddress constructor. - * - * @param MaskedQuoteIdToQuoteId $maskedQuoteIdToQuoteId - * @param CartRepositoryInterface $cartRepository * @param AddressDataProvider $addressDataProvider */ public function __construct( - MaskedQuoteIdToQuoteId $maskedQuoteIdToQuoteId, - CartRepositoryInterface $cartRepository, AddressDataProvider $addressDataProvider ) { - $this->maskedQuoteIdToQuoteId = $maskedQuoteIdToQuoteId; - $this->cartRepository = $cartRepository; $this->addressDataProvider = $addressDataProvider; } diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index ce982952f1ae..a6c56318d7a9 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -95,7 +95,7 @@ type Cart { cart_id: String items: [CartItemInterface] applied_coupon: AppliedCoupon - addresses: [CartAddress]! @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\CartAddress") + addresses: [CartAddress]! @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\CartAddresses") } type CartAddress { From 4a8720485da929caa4301fdac430f91a3628d42d Mon Sep 17 00:00:00 2001 From: Valeriy Nayda Date: Tue, 13 Nov 2018 16:18:08 +0200 Subject: [PATCH 12/12] GraphQl-39: Manage Shipping methods on Cart - Fix static tests --- .../Magento/QuoteGraphQl/Model/Cart/SetShippingMethodOnCart.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingMethodOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingMethodOnCart.php index 7f945dca2fd7..a630b2d07c7d 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingMethodOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingMethodOnCart.php @@ -98,4 +98,4 @@ public function execute(Quote $cart, int $cartAddressId, string $carrierCode, st throw new GraphQlInputException(__($exception->getMessage())); } } -} \ No newline at end of file +}