From d8e984629ee68872b34f75d82279262d182b4373 Mon Sep 17 00:00:00 2001 From: Alex Paliarush Date: Mon, 30 Jul 2018 21:49:16 -0500 Subject: [PATCH 01/22] Initial draft for add items to cart GraphQL mutations --- .../graph-ql/coverage/add-items-to-cart.md | 49 ++++++++++++ .../AddBundleProductToCart.graphqls | 48 +++++++++++ .../AddConfigurableProductToCart.graphqls | 37 +++++++++ .../AddDownloadableProductToCart.graphqls | 34 ++++++++ .../AddGiftCardProductToCart.graphqls | 30 +++++++ .../AddGroupedProductToCart.graphqls | 18 +++++ .../AddSimpleProductToCart.graphqls | 79 +++++++++++++++++++ .../AddVirtualProductToCart.graphqls | 24 ++++++ 8 files changed, 319 insertions(+) create mode 100644 design-documents/graph-ql/coverage/add-items-to-cart.md create mode 100644 design-documents/graph-ql/coverage/add-items-to-cart/AddBundleProductToCart.graphqls create mode 100644 design-documents/graph-ql/coverage/add-items-to-cart/AddConfigurableProductToCart.graphqls create mode 100644 design-documents/graph-ql/coverage/add-items-to-cart/AddDownloadableProductToCart.graphqls create mode 100644 design-documents/graph-ql/coverage/add-items-to-cart/AddGiftCardProductToCart.graphqls create mode 100644 design-documents/graph-ql/coverage/add-items-to-cart/AddGroupedProductToCart.graphqls create mode 100644 design-documents/graph-ql/coverage/add-items-to-cart/AddSimpleProductToCart.graphqls create mode 100644 design-documents/graph-ql/coverage/add-items-to-cart/AddVirtualProductToCart.graphqls diff --git a/design-documents/graph-ql/coverage/add-items-to-cart.md b/design-documents/graph-ql/coverage/add-items-to-cart.md new file mode 100644 index 000000000..819d2f6d3 --- /dev/null +++ b/design-documents/graph-ql/coverage/add-items-to-cart.md @@ -0,0 +1,49 @@ +**Overview** + +As a Magento developer, I need to manipulate the shopping cart via GraphQL so that I can build basic ecommerce experiences for shoppers on the front-end using only GraphQL. + +GraphQL needs to provide sufficient mutations (ways to create/update/delete data) for a developer to build out the storefront checkout experience for a shopper. + +**Use cases:** +- Both guest and registered shoppers can add new items to cart +- Both guest and registered shoppers can update item qty in cart +- Both guest and registered shoppers can remove items from cart +- Both guest and registered shoppers can update the configuration (for a configurable product) or quantity of a previously added configurable product in cart + - Edit Item link > Product page > Update configuration or qty > Update Cart + +**Main decision points:** + +- Separate mutations for each product type while adding items to cart. Each operation will be supporting bulk use case +- Uniform interface for guest vs customer +- Separate mutations for each checkout step + - Create empty cart + - Add items to cart + - Set shipment method + - Set payment method + - Set addresses + - Same granularity for updates and removals +- Possibility to combine mutations for checkout steps +- Can create "order in one call" mutation in the future if needed +- Hashed IDs for cart items +- Single input object +- Async nature of the flow must be supported on the client side (via AJAX calls) + + +**Open questions:** + +- Do we want to implement server-side asynchronous mutations by default? + +**Proposed schema for adding items to cart:** + +- [AddSimpleProductToCart](AddSimpleProductToCart.graphqls) +- [AddBundleProductToCart](AddBundleProductToCart.graphqls) +- [AddConfigurableProductToCart](AddConfigurableProductToCart.graphqls) +- [AddDownloadableProductToCart](AddDownloadableProductToCart.graphqls) +- [AddGiftCardProductToCart](AddGiftCardProductToCart.graphqls) +- [AddGroupedProductToCart](AddGroupedProductToCart.graphqls) +- [AddVirtualProductToCart](AddVirtualProductToCart.graphqls) + + +**My Account area impacted:** +- Cart +- Minicart diff --git a/design-documents/graph-ql/coverage/add-items-to-cart/AddBundleProductToCart.graphqls b/design-documents/graph-ql/coverage/add-items-to-cart/AddBundleProductToCart.graphqls new file mode 100644 index 000000000..025379d03 --- /dev/null +++ b/design-documents/graph-ql/coverage/add-items-to-cart/AddBundleProductToCart.graphqls @@ -0,0 +1,48 @@ +type Mutation { + addBundleProductsToCart(input: AddBundleProductsToCartInput): AddBundleProductsToCartOutput +} + +input AddBundleProductsToCartInput { + cartId: String! + cartItems: [BundleProductCartItemInput!]! +} + +input BundleProductCartItemInput { + sku: String! + quantity: Float! + bundle_options:[BundleOptionInput!]! + customizable_options:[CustomizableOptionInput!] +} + +input BundleOptionInput { + id: Int! + quantity: Float! + value: [String!]! +} + +type AddBundleProductsToCartOutput { + cart: Cart +} + +type BundleCartItem implements CartItemInterface { + customizable_options: [SelectedCustomizableOption]! + bundle_options: [SelectedBundleOption!]! +} + +type SelectedBundleOption { + id: Int! + label: String! + type: String! + # No quantity here even though it is set on option level in the input + values: [SelectedBundleOptionValue!]! + sort_order: Int! +} + +type SelectedBundleOptionValue { + id: Int! + label: String! + quantity: Float! # Quantity is displayed on option value level, while is set on option level + price: CartItemSelectedOptionValuePrice! + sort_order: Int! +} + diff --git a/design-documents/graph-ql/coverage/add-items-to-cart/AddConfigurableProductToCart.graphqls b/design-documents/graph-ql/coverage/add-items-to-cart/AddConfigurableProductToCart.graphqls new file mode 100644 index 000000000..b642f3291 --- /dev/null +++ b/design-documents/graph-ql/coverage/add-items-to-cart/AddConfigurableProductToCart.graphqls @@ -0,0 +1,37 @@ +type Mutation { + addConfigurableProductsToCart(input: AddConfigurableProductsToCartInput): AddConfigurableProductsToCartOutput +} + +input AddConfigurableProductsToCartInput { + cartId: String! + cartItems: [ConfigurableProductCartItemInput!]! +} + +input ConfigurableProductCartItemInput { + sku: String! + quantity: Float! + configurable_options:[ConfigurableOptionInput!]! + customizable_options:[CustomizableOptionInput!] +} + +input ConfigurableOptionInput { + id: Int! + value: Int! +} + +type AddConfigurableProductsToCartOutput { + cart: Cart +} + +type ConfigurableCartItem implements CartItemInterface { + customizable_options: [SelectedCustomizableOption]! + configurable_options: [SelectedConfigurableOption!]! +} + +type SelectedConfigurableOption { + id: Int! + option_label: String! + value_id: Int! + value_label: String! +} + diff --git a/design-documents/graph-ql/coverage/add-items-to-cart/AddDownloadableProductToCart.graphqls b/design-documents/graph-ql/coverage/add-items-to-cart/AddDownloadableProductToCart.graphqls new file mode 100644 index 000000000..0abf2590b --- /dev/null +++ b/design-documents/graph-ql/coverage/add-items-to-cart/AddDownloadableProductToCart.graphqls @@ -0,0 +1,34 @@ +type Mutation { + addDownloadableProductsToCart(input: AddDownloadableProductsToCartInput): AddDownloadableProductsToCartOutput +} + +input AddDownloadableProductsToCartInput { + cartId: String! + cartItems: [DownloadableProductCartItemInput!]! +} + +input DownloadableProductCartItemInput { + sku: String! + quantity: Int! + downloadable_links: [DownloadableLinksInput!] + customizable_options:[CustomizableOptionInput!] +} + +input DownloadableLinksInput { + id: [Int!]! +} + +type AddDownloadableProductsToCartOutput { + cart: Cart +} + +type DownloadableCartItem implements CartItemInterface { + links_label: String! + links: [DownloadableCartItemLink!]! + configurable_options: [SelectedConfigurableOption!]! +} + +type DownloadableCartItemLink { + id: Int! + label: String! +} diff --git a/design-documents/graph-ql/coverage/add-items-to-cart/AddGiftCardProductToCart.graphqls b/design-documents/graph-ql/coverage/add-items-to-cart/AddGiftCardProductToCart.graphqls new file mode 100644 index 000000000..8971c8e93 --- /dev/null +++ b/design-documents/graph-ql/coverage/add-items-to-cart/AddGiftCardProductToCart.graphqls @@ -0,0 +1,30 @@ +type Mutation { + addGiftCardProductsToCart(input: AddGiftCardProductsToCartInput): AddGiftCardProductsToCartOutput +} + +input AddGiftCardProductsToCartInput { + cartId: String! + cartItems: [GiftCardProductCartItemInput!]! +} + +input GiftCardProductCartItemInput { + sku: String! + quantity: Float! + sender_name: String! + recepient_name: String! + amount: Money! + message: String + customizable_options:[CustomizableOptionInput!] +} + +type AddGiftCardProductsToCartOutput { + cart: Cart +} + +type ConfigurableCartItem implements CartItemInterface { + sender_name: String! + recepient_name: String! + amount: Money! + message: String + customizable_options: [SelectedCustomizableOption]! +} diff --git a/design-documents/graph-ql/coverage/add-items-to-cart/AddGroupedProductToCart.graphqls b/design-documents/graph-ql/coverage/add-items-to-cart/AddGroupedProductToCart.graphqls new file mode 100644 index 000000000..8addc6fe1 --- /dev/null +++ b/design-documents/graph-ql/coverage/add-items-to-cart/AddGroupedProductToCart.graphqls @@ -0,0 +1,18 @@ +type Mutation { + addGroupedProductsToCart(input: AddGroupedProductsToCartInput): AddGroupedProductsToCartOutput +} + +input AddGroupedProductsToCartInput { + cartId: String! + cartItems: [GroupedProductCartItemInput!]! +} + +input GroupedProductCartItemInput { + sku: String! + quantity: Float! + # the difference from simple products is that grouped products do not support customizable options +} + +type AddGroupedProductsToCartOutput { + cart: Cart +} diff --git a/design-documents/graph-ql/coverage/add-items-to-cart/AddSimpleProductToCart.graphqls b/design-documents/graph-ql/coverage/add-items-to-cart/AddSimpleProductToCart.graphqls new file mode 100644 index 000000000..2d0fbc27f --- /dev/null +++ b/design-documents/graph-ql/coverage/add-items-to-cart/AddSimpleProductToCart.graphqls @@ -0,0 +1,79 @@ +type Mutation { + addSimpleProductsToCart(input: AddSimpleProductsToCartInput): AddSimpleProductsToCartOutput +} + +input AddSimpleProductsToCartInput { + cartId: String! + cartItems: [SimpleProductCartItemInput!]! +} + +input SimpleProductCartItemInput { + sku: String! + quantity: Float! + customizable_options:[CustomizableOptionInput!] +} + +input CustomizableOptionInput { + id: Int! + value: String! +} + +type AddSimpleProductsToCartOutput { + cart: Cart +} + +type Cart { + id: String + items: [CartItemInterface] +} + +interface CartItemInterface @typeResolver(class: "Magento\\CatalogCheckoutGraphQl\\Model\\CartItemInterfaceTypeResolverComposite") { + id: Int! + qty: Float! + product: CartItemProduct! + image: CartItemImage! + prices: CartItemPrices! +} + +type CartItemImage { + alt_text: String! + thumbnail: ProductMediaGalleryEntriesContent! +} + +type CartItemPrices { + price: Money! + subtotal: Money! +} + +type CartItemProduct { + sku: String! + name: String! + type: String! + # Does it make sense to provide several basic fields (with potentially better performance) in addition to the ful product object? + details: ProductInterface! +} + +type SimpleCartItem implements CartItemInterface { + customizable_options: [SelectedCustomizableOption] +} + +type SelectedCustomizableOption { + id: Int! + label: String! + type: String! + values: [SelectedCustomizableOptionValue!]! + sort_order: Int! +} + +type SelectedCustomizableOptionValue { + id: Int + label: String! + price: CartItemSelectedOptionValuePrice! + sort_order: Int! +} + +type CartItemSelectedOptionValuePrice { + value: Float! + units: String! + type: PriceTypeEnum! +} diff --git a/design-documents/graph-ql/coverage/add-items-to-cart/AddVirtualProductToCart.graphqls b/design-documents/graph-ql/coverage/add-items-to-cart/AddVirtualProductToCart.graphqls new file mode 100644 index 000000000..7376d56a7 --- /dev/null +++ b/design-documents/graph-ql/coverage/add-items-to-cart/AddVirtualProductToCart.graphqls @@ -0,0 +1,24 @@ +type Mutation { + # for now this mutation is identical to addSimpleProductsToCart and exists as a syntax sugar. Also it allows product type based customizations + addVirtualProductsToCart(input: AddVirtualProductsToCartInput): AddVirtualProductsToCartOutput +} + +input AddVirtualProductsToCartInput { + cartId: String! + cartItems: [VirtualProductCartItemInput!]! +} + +input VirtualProductCartItemInput { + sku: String! + quantity: Float! + customizable_options:[CustomizableOptionInput!] +} + +type AddVirtualProductsToCartOutput { + cart: Cart +} + +# Custom cart item type can be used to customize rendering when there are no physical producs available, e.g. skip shipping +type VirtualCartItem implements CartItemInterface { + customizable_options: [SelectedCustomizableOption] +} From 30ef22fb752f6fb704501a44c01dafabdaee4c6c Mon Sep 17 00:00:00 2001 From: Alex Paliarush Date: Tue, 31 Jul 2018 14:02:46 -0500 Subject: [PATCH 02/22] Update AddGiftCardProductToCart.graphqls --- .../add-items-to-cart/AddGiftCardProductToCart.graphqls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/design-documents/graph-ql/coverage/add-items-to-cart/AddGiftCardProductToCart.graphqls b/design-documents/graph-ql/coverage/add-items-to-cart/AddGiftCardProductToCart.graphqls index 8971c8e93..9b1e76fa0 100644 --- a/design-documents/graph-ql/coverage/add-items-to-cart/AddGiftCardProductToCart.graphqls +++ b/design-documents/graph-ql/coverage/add-items-to-cart/AddGiftCardProductToCart.graphqls @@ -21,7 +21,7 @@ type AddGiftCardProductsToCartOutput { cart: Cart } -type ConfigurableCartItem implements CartItemInterface { +type GiftCardCartItem implements CartItemInterface { sender_name: String! recepient_name: String! amount: Money! From dc065232dec38335a18ab0f14614ee064ce361ac Mon Sep 17 00:00:00 2001 From: Alex Paliarush Date: Thu, 2 Aug 2018 09:52:41 -0500 Subject: [PATCH 03/22] Eliminated CartItemProduct and CartItemImage. All data can be fetched using ProductInterface --- .../AddSimpleProductToCart.graphqls | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/design-documents/graph-ql/coverage/add-items-to-cart/AddSimpleProductToCart.graphqls b/design-documents/graph-ql/coverage/add-items-to-cart/AddSimpleProductToCart.graphqls index 2d0fbc27f..b75a39ee5 100644 --- a/design-documents/graph-ql/coverage/add-items-to-cart/AddSimpleProductToCart.graphqls +++ b/design-documents/graph-ql/coverage/add-items-to-cart/AddSimpleProductToCart.graphqls @@ -30,29 +30,15 @@ type Cart { interface CartItemInterface @typeResolver(class: "Magento\\CatalogCheckoutGraphQl\\Model\\CartItemInterfaceTypeResolverComposite") { id: Int! qty: Float! - product: CartItemProduct! - image: CartItemImage! + product: ProductInterface! prices: CartItemPrices! } -type CartItemImage { - alt_text: String! - thumbnail: ProductMediaGalleryEntriesContent! -} - type CartItemPrices { price: Money! subtotal: Money! } -type CartItemProduct { - sku: String! - name: String! - type: String! - # Does it make sense to provide several basic fields (with potentially better performance) in addition to the ful product object? - details: ProductInterface! -} - type SimpleCartItem implements CartItemInterface { customizable_options: [SelectedCustomizableOption] } From d638fa9874b9484a5ecdd913780f96b68644639e Mon Sep 17 00:00:00 2001 From: Alex Paliarush Date: Thu, 2 Aug 2018 19:33:06 -0500 Subject: [PATCH 04/22] Added coupon codes support --- .../coverage/CouponOperations.graphqls | 30 +++++++++++++++++++ .../AddBundleProductToCart.graphqls | 4 +-- .../AddConfigurableProductToCart.graphqls | 4 +-- .../AddDownloadableProductToCart.graphqls | 4 +-- .../AddGiftCardProductToCart.graphqls | 4 +-- .../AddGroupedProductToCart.graphqls | 4 +-- .../AddSimpleProductToCart.graphqls | 4 +-- .../AddVirtualProductToCart.graphqls | 4 +-- 8 files changed, 44 insertions(+), 14 deletions(-) create mode 100644 design-documents/graph-ql/coverage/CouponOperations.graphqls diff --git a/design-documents/graph-ql/coverage/CouponOperations.graphqls b/design-documents/graph-ql/coverage/CouponOperations.graphqls new file mode 100644 index 000000000..1bdbc6f79 --- /dev/null +++ b/design-documents/graph-ql/coverage/CouponOperations.graphqls @@ -0,0 +1,30 @@ +type Mutation { + applyCouponToCart(input: ApplyCouponToCartInput): ApplyCouponToCartOutput + cancelCouponForCart(input: CancelCouponForCartInput): CancelCouponForCartOutput +} + +input ApplyCouponToCartInput { + cart_id: String! + coupon_code: String! +} + +type ApplyCouponToCartOutput { + cart: Cart! +} + +type Cart { + applied_coupon: AppliedCoupon +} + +type AppliedCoupon { + # Wrapper allows for future extension of coupon info + code: String! +} + +input CancelCouponForCartInput { + cart_id: String! +} + +type CancelCouponForCartOutput { + cart: Cart +} diff --git a/design-documents/graph-ql/coverage/add-items-to-cart/AddBundleProductToCart.graphqls b/design-documents/graph-ql/coverage/add-items-to-cart/AddBundleProductToCart.graphqls index 025379d03..674dd0806 100644 --- a/design-documents/graph-ql/coverage/add-items-to-cart/AddBundleProductToCart.graphqls +++ b/design-documents/graph-ql/coverage/add-items-to-cart/AddBundleProductToCart.graphqls @@ -3,7 +3,7 @@ type Mutation { } input AddBundleProductsToCartInput { - cartId: String! + cart_id: String! cartItems: [BundleProductCartItemInput!]! } @@ -21,7 +21,7 @@ input BundleOptionInput { } type AddBundleProductsToCartOutput { - cart: Cart + cart: Cart! } type BundleCartItem implements CartItemInterface { diff --git a/design-documents/graph-ql/coverage/add-items-to-cart/AddConfigurableProductToCart.graphqls b/design-documents/graph-ql/coverage/add-items-to-cart/AddConfigurableProductToCart.graphqls index b642f3291..e1e4af9d9 100644 --- a/design-documents/graph-ql/coverage/add-items-to-cart/AddConfigurableProductToCart.graphqls +++ b/design-documents/graph-ql/coverage/add-items-to-cart/AddConfigurableProductToCart.graphqls @@ -3,7 +3,7 @@ type Mutation { } input AddConfigurableProductsToCartInput { - cartId: String! + cart_id: String! cartItems: [ConfigurableProductCartItemInput!]! } @@ -20,7 +20,7 @@ input ConfigurableOptionInput { } type AddConfigurableProductsToCartOutput { - cart: Cart + cart: Cart! } type ConfigurableCartItem implements CartItemInterface { diff --git a/design-documents/graph-ql/coverage/add-items-to-cart/AddDownloadableProductToCart.graphqls b/design-documents/graph-ql/coverage/add-items-to-cart/AddDownloadableProductToCart.graphqls index 0abf2590b..6e36938ec 100644 --- a/design-documents/graph-ql/coverage/add-items-to-cart/AddDownloadableProductToCart.graphqls +++ b/design-documents/graph-ql/coverage/add-items-to-cart/AddDownloadableProductToCart.graphqls @@ -3,7 +3,7 @@ type Mutation { } input AddDownloadableProductsToCartInput { - cartId: String! + cart_id: String! cartItems: [DownloadableProductCartItemInput!]! } @@ -19,7 +19,7 @@ input DownloadableLinksInput { } type AddDownloadableProductsToCartOutput { - cart: Cart + cart: Cart! } type DownloadableCartItem implements CartItemInterface { diff --git a/design-documents/graph-ql/coverage/add-items-to-cart/AddGiftCardProductToCart.graphqls b/design-documents/graph-ql/coverage/add-items-to-cart/AddGiftCardProductToCart.graphqls index 9b1e76fa0..e8e408e20 100644 --- a/design-documents/graph-ql/coverage/add-items-to-cart/AddGiftCardProductToCart.graphqls +++ b/design-documents/graph-ql/coverage/add-items-to-cart/AddGiftCardProductToCart.graphqls @@ -3,7 +3,7 @@ type Mutation { } input AddGiftCardProductsToCartInput { - cartId: String! + cart_id: String! cartItems: [GiftCardProductCartItemInput!]! } @@ -18,7 +18,7 @@ input GiftCardProductCartItemInput { } type AddGiftCardProductsToCartOutput { - cart: Cart + cart: Cart! } type GiftCardCartItem implements CartItemInterface { diff --git a/design-documents/graph-ql/coverage/add-items-to-cart/AddGroupedProductToCart.graphqls b/design-documents/graph-ql/coverage/add-items-to-cart/AddGroupedProductToCart.graphqls index 8addc6fe1..322a99a70 100644 --- a/design-documents/graph-ql/coverage/add-items-to-cart/AddGroupedProductToCart.graphqls +++ b/design-documents/graph-ql/coverage/add-items-to-cart/AddGroupedProductToCart.graphqls @@ -3,7 +3,7 @@ type Mutation { } input AddGroupedProductsToCartInput { - cartId: String! + cart_id: String! cartItems: [GroupedProductCartItemInput!]! } @@ -14,5 +14,5 @@ input GroupedProductCartItemInput { } type AddGroupedProductsToCartOutput { - cart: Cart + cart: Cart! } diff --git a/design-documents/graph-ql/coverage/add-items-to-cart/AddSimpleProductToCart.graphqls b/design-documents/graph-ql/coverage/add-items-to-cart/AddSimpleProductToCart.graphqls index b75a39ee5..b17697ee1 100644 --- a/design-documents/graph-ql/coverage/add-items-to-cart/AddSimpleProductToCart.graphqls +++ b/design-documents/graph-ql/coverage/add-items-to-cart/AddSimpleProductToCart.graphqls @@ -3,7 +3,7 @@ type Mutation { } input AddSimpleProductsToCartInput { - cartId: String! + cart_id: String! cartItems: [SimpleProductCartItemInput!]! } @@ -19,7 +19,7 @@ input CustomizableOptionInput { } type AddSimpleProductsToCartOutput { - cart: Cart + cart: Cart! } type Cart { diff --git a/design-documents/graph-ql/coverage/add-items-to-cart/AddVirtualProductToCart.graphqls b/design-documents/graph-ql/coverage/add-items-to-cart/AddVirtualProductToCart.graphqls index 7376d56a7..1316d95f9 100644 --- a/design-documents/graph-ql/coverage/add-items-to-cart/AddVirtualProductToCart.graphqls +++ b/design-documents/graph-ql/coverage/add-items-to-cart/AddVirtualProductToCart.graphqls @@ -4,7 +4,7 @@ type Mutation { } input AddVirtualProductsToCartInput { - cartId: String! + cart_id: String! cartItems: [VirtualProductCartItemInput!]! } @@ -15,7 +15,7 @@ input VirtualProductCartItemInput { } type AddVirtualProductsToCartOutput { - cart: Cart + cart: Cart! } # Custom cart item type can be used to customize rendering when there are no physical producs available, e.g. skip shipping From 902e999437730f92fc10edbca03699d01b48b0e5 Mon Sep 17 00:00:00 2001 From: Alex Paliarush Date: Fri, 3 Aug 2018 14:24:01 -0500 Subject: [PATCH 05/22] Renamed coupon removal mutation --- .../graph-ql/coverage/CouponOperations.graphqls | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/design-documents/graph-ql/coverage/CouponOperations.graphqls b/design-documents/graph-ql/coverage/CouponOperations.graphqls index 1bdbc6f79..0f31072ae 100644 --- a/design-documents/graph-ql/coverage/CouponOperations.graphqls +++ b/design-documents/graph-ql/coverage/CouponOperations.graphqls @@ -1,6 +1,6 @@ type Mutation { applyCouponToCart(input: ApplyCouponToCartInput): ApplyCouponToCartOutput - cancelCouponForCart(input: CancelCouponForCartInput): CancelCouponForCartOutput + removeCouponFromCart(input: RemoveCouponFromCartInput): RemoveCouponFromCartOutput } input ApplyCouponToCartInput { @@ -21,10 +21,10 @@ type AppliedCoupon { code: String! } -input CancelCouponForCartInput { +input RemoveCouponFromCartInput { cart_id: String! } -type CancelCouponForCartOutput { +type RemoveCouponFromCartOutput { cart: Cart } From 43829b3df57404b1df8d9e3805343d1dae53d0cc Mon Sep 17 00:00:00 2001 From: Alex Paliarush Date: Mon, 6 Aug 2018 09:40:41 -0500 Subject: [PATCH 06/22] Reworded "overview" in the readme --- design-documents/graph-ql/coverage/add-items-to-cart.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/design-documents/graph-ql/coverage/add-items-to-cart.md b/design-documents/graph-ql/coverage/add-items-to-cart.md index 819d2f6d3..809a897ce 100644 --- a/design-documents/graph-ql/coverage/add-items-to-cart.md +++ b/design-documents/graph-ql/coverage/add-items-to-cart.md @@ -1,6 +1,6 @@ **Overview** -As a Magento developer, I need to manipulate the shopping cart via GraphQL so that I can build basic ecommerce experiences for shoppers on the front-end using only GraphQL. +As a Magento developer, I need to manipulate the shopping cart via GraphQL so that I can programmatically create orders on behalf of a shopper. GraphQL needs to provide sufficient mutations (ways to create/update/delete data) for a developer to build out the storefront checkout experience for a shopper. From 7d71efc7f9950b8e97ec35054028f418c22d6dc4 Mon Sep 17 00:00:00 2001 From: Alex Paliarush Date: Tue, 7 Aug 2018 11:21:33 -0500 Subject: [PATCH 07/22] Proposals for input and output wrappers --- .../coverage/input-optput-wrappers.md | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 design-documents/graph-ql/coverage/input-optput-wrappers.md diff --git a/design-documents/graph-ql/coverage/input-optput-wrappers.md b/design-documents/graph-ql/coverage/input-optput-wrappers.md new file mode 100644 index 000000000..238c34cb7 --- /dev/null +++ b/design-documents/graph-ql/coverage/input-optput-wrappers.md @@ -0,0 +1,61 @@ +**Problem statement** + +Current way of declaring queries and mutations limits extensibility and deprecation capabilities. + +```$graphqls +type Mutation { + generateCustomerToken( + email: String!, + password: String! + ): String! +} +``` + +Such declaration has several issues: +- It is not possible to extend or modify the list of arguments from 3rd party extension. Schema stitching mechanism performs merge on types level only +- There is no way to add extra data to the output of the mutation in a backward compatible way +- It is not possible to evolve arguments list and output type of our API in the future. Even if we decide to do breaking changes, there is no way to deprecate existing arguments and output data first + + +**Proposed solution: Wrappers for arguments and output** + +Wrappers for arguments and output type can solve extensibility and deprecation issues. + +```$graphqls +type Mutation { + generateCustomerToken(input: GenerateCustomerTokenInput!): GenerateCustomerTokenOutput! +} + +input GenerateCustomerTokenInput { + email: String! + password: String! +} + +type GenerateCustomerTokenOutput { + token: String! +} +``` + +With such schema it is easy to extend the list of arguments. For example, if system integrator got a new requirement to enable multi-factor authentication, the schema can be extended from 3rd party module to support this requirement as follows. + +```$graphqls +input GenerateCustomerTokenInput { + multi_factor_auth_token: String! +} +``` +Additionally, plugin can be added for the `generateCustomerToken` mutation resolver to implement additional verification step. + +Now let's assume that client app needs to know when to request a new token. One of the solutions could be to return token TTL along with its value. +The output data can be extended in this case as follows: +```$graphqls +input GenerateCustomerTokenOutput { + token_ttl: String! +} +``` +The `token_ttl` value can be populated via new resolver for this field or from the plugin on existing `generateCustomerToken` mutation resolver. + + +**Action items** + +1. Modify schema for all existing queries and mutations +1. Make sure that this requirement is documented and followed for the new GraphQL coverage From 83aa88032abc6265affc3cc5ab517c71e2fbf422 Mon Sep 17 00:00:00 2001 From: Alex Paliarush Date: Tue, 7 Aug 2018 19:04:50 -0500 Subject: [PATCH 08/22] Schema for cart prices --- .../graph-ql/coverage/CartPrices.graphqls | 57 +++++++++++++++++++ .../AddSimpleProductToCart.graphqls | 6 -- 2 files changed, 57 insertions(+), 6 deletions(-) create mode 100644 design-documents/graph-ql/coverage/CartPrices.graphqls diff --git a/design-documents/graph-ql/coverage/CartPrices.graphqls b/design-documents/graph-ql/coverage/CartPrices.graphqls new file mode 100644 index 000000000..60509a626 --- /dev/null +++ b/design-documents/graph-ql/coverage/CartPrices.graphqls @@ -0,0 +1,57 @@ +interface CartItemInterface { + prices: CartItemTotals +} + +type Cart { + prices: CartTotals + addresses: [CartAddress] + # additional fields will be added later +} + +type CartAddress { + pices: CartAddressTotals + # additional fields will be added later +} + +interface CartPricesInterface { + grand_total: Money + + # price display settings should be requested via store config query + subtotal_including_tax: Money + subtotal_excluding_tax: Money + + subtotal_with_discount_excluding_tax: Money + discount_tax_compensation: Money #Should we have subtotal with discount including tax instead, is it different from grand_total? + + applied_taxes: [CartTaxItem]! # Should include regular taxes and WEEE taxes + applied_discounts: [CartDiscountItem]! +} + +type CartItemPrices implements CartPricesInterface { + price_including_tax: Money + price_excluding_tax: Money + + custom_price: Money +} + +type CartAddressPrices implements CartPricesInterface { + + shipping_including_tax: Money + shipping_excluding_tax: Money + + shipping_discount: Money # Do we need shipping_with_discount_including_tax? + shipping_discount_tax_compensation: Money +} + +type CartPrices implements CartPricesInterface { +} + +type CartTaxItem { + amount: Money! + label: String! +} + +type CartDiscountItem { + amount: Money! + label: String! +} diff --git a/design-documents/graph-ql/coverage/add-items-to-cart/AddSimpleProductToCart.graphqls b/design-documents/graph-ql/coverage/add-items-to-cart/AddSimpleProductToCart.graphqls index b17697ee1..96e6f5c42 100644 --- a/design-documents/graph-ql/coverage/add-items-to-cart/AddSimpleProductToCart.graphqls +++ b/design-documents/graph-ql/coverage/add-items-to-cart/AddSimpleProductToCart.graphqls @@ -31,12 +31,6 @@ interface CartItemInterface @typeResolver(class: "Magento\\CatalogCheckoutGraphQ id: Int! qty: Float! product: ProductInterface! - prices: CartItemPrices! -} - -type CartItemPrices { - price: Money! - subtotal: Money! } type SimpleCartItem implements CartItemInterface { From c6f7d8483489fb47a90372154bdd60617e9e9509 Mon Sep 17 00:00:00 2001 From: Alex Paliarush Date: Wed, 8 Aug 2018 09:57:08 -0500 Subject: [PATCH 09/22] Fixed typos in schema for cart prices --- design-documents/graph-ql/coverage/CartPrices.graphqls | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/design-documents/graph-ql/coverage/CartPrices.graphqls b/design-documents/graph-ql/coverage/CartPrices.graphqls index 60509a626..ab83af2df 100644 --- a/design-documents/graph-ql/coverage/CartPrices.graphqls +++ b/design-documents/graph-ql/coverage/CartPrices.graphqls @@ -1,15 +1,15 @@ interface CartItemInterface { - prices: CartItemTotals + prices: CartItemPrices } type Cart { - prices: CartTotals + prices: CartPrices addresses: [CartAddress] # additional fields will be added later } type CartAddress { - pices: CartAddressTotals + prices: CartAddressPrices # additional fields will be added later } From 47b9607f10c2a4fb7b1413acec64a6af91356ba8 Mon Sep 17 00:00:00 2001 From: Alex Paliarush Date: Wed, 8 Aug 2018 18:40:56 -0500 Subject: [PATCH 10/22] Added basic fields for cart type --- .../graph-ql/coverage/Cart.graphqls | 33 +++++++++++++++++++ .../graph-ql/coverage/CartPrices.graphqls | 2 -- .../graph-ql/coverage/add-items-to-cart.md | 6 +--- .../AddSimpleProductToCart.graphqls | 1 - .../coverage/input-optput-wrappers.md | 6 +++- 5 files changed, 39 insertions(+), 9 deletions(-) create mode 100644 design-documents/graph-ql/coverage/Cart.graphqls diff --git a/design-documents/graph-ql/coverage/Cart.graphqls b/design-documents/graph-ql/coverage/Cart.graphqls new file mode 100644 index 000000000..130b2939b --- /dev/null +++ b/design-documents/graph-ql/coverage/Cart.graphqls @@ -0,0 +1,33 @@ +type Query { + cart(input: CartQueryInput): CartQueryOutput +} + +input CartQueryInput { + cart_id: String! +} + +type CartQueryOutput { + cart: Cart +} + +type Cart { + id: String! + addresses: [CartAddress] + line_items_count: Int! + items_quantity: Float! + selected_payment_method: SelectedPaymentMethod +} + +type SelectedPaymentMethod { + code: String! + label: String! +} + +type CartAddress { + selected_shipping_method: SelectedShippingMethod +} + +type SelectedShippingMethod { + code: String! + label: String! +} diff --git a/design-documents/graph-ql/coverage/CartPrices.graphqls b/design-documents/graph-ql/coverage/CartPrices.graphqls index ab83af2df..2c6ed116f 100644 --- a/design-documents/graph-ql/coverage/CartPrices.graphqls +++ b/design-documents/graph-ql/coverage/CartPrices.graphqls @@ -4,8 +4,6 @@ interface CartItemInterface { type Cart { prices: CartPrices - addresses: [CartAddress] - # additional fields will be added later } type CartAddress { diff --git a/design-documents/graph-ql/coverage/add-items-to-cart.md b/design-documents/graph-ql/coverage/add-items-to-cart.md index 809a897ce..550948628 100644 --- a/design-documents/graph-ql/coverage/add-items-to-cart.md +++ b/design-documents/graph-ql/coverage/add-items-to-cart.md @@ -27,11 +27,7 @@ GraphQL needs to provide sufficient mutations (ways to create/update/delete data - Hashed IDs for cart items - Single input object - Async nature of the flow must be supported on the client side (via AJAX calls) - - -**Open questions:** - -- Do we want to implement server-side asynchronous mutations by default? +- Server-side asynchronous mutations can be supported in the future on framework level in a way similar to Async REST. **Proposed schema for adding items to cart:** diff --git a/design-documents/graph-ql/coverage/add-items-to-cart/AddSimpleProductToCart.graphqls b/design-documents/graph-ql/coverage/add-items-to-cart/AddSimpleProductToCart.graphqls index 96e6f5c42..6738e8466 100644 --- a/design-documents/graph-ql/coverage/add-items-to-cart/AddSimpleProductToCart.graphqls +++ b/design-documents/graph-ql/coverage/add-items-to-cart/AddSimpleProductToCart.graphqls @@ -23,7 +23,6 @@ type AddSimpleProductsToCartOutput { } type Cart { - id: String items: [CartItemInterface] } diff --git a/design-documents/graph-ql/coverage/input-optput-wrappers.md b/design-documents/graph-ql/coverage/input-optput-wrappers.md index 238c34cb7..d2a4e929b 100644 --- a/design-documents/graph-ql/coverage/input-optput-wrappers.md +++ b/design-documents/graph-ql/coverage/input-optput-wrappers.md @@ -58,4 +58,8 @@ The `token_ttl` value can be populated via new resolver for this field or from t **Action items** 1. Modify schema for all existing queries and mutations -1. Make sure that this requirement is documented and followed for the new GraphQL coverage +1. Make sure that this requirement is documented and followed for the new GraphQL coverage + +**Open questions** + +1. What can be one about queries and mutations, which do not require arguments at the moment? We cannot create wrapper type without fields. Would be nice to enable extensibility for sich argument lists as well. From bdd70b474e40ba53a38393743c1d03b5574398ec Mon Sep 17 00:00:00 2001 From: Alex Paliarush Date: Thu, 9 Aug 2018 17:47:24 -0500 Subject: [PATCH 11/22] Adjusted cart schema --- .../graph-ql/coverage/Cart.graphqls | 36 ++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/design-documents/graph-ql/coverage/Cart.graphqls b/design-documents/graph-ql/coverage/Cart.graphqls index 130b2939b..1d561609f 100644 --- a/design-documents/graph-ql/coverage/Cart.graphqls +++ b/design-documents/graph-ql/coverage/Cart.graphqls @@ -15,19 +15,47 @@ type Cart { addresses: [CartAddress] line_items_count: Int! items_quantity: Float! - selected_payment_method: SelectedPaymentMethod + selected_payment_method: CheckoutPaymentMethod + available_payment_methods: [CheckoutPaymentMethod]! } -type SelectedPaymentMethod { +type CheckoutPaymentMethod { code: String! label: String! } type CartAddress { - selected_shipping_method: SelectedShippingMethod + selected_shipping_method: CheckoutShippingMethod + available_shipping_methods: [CheckoutShippingMethod]! + + address_type: AdressTypeEnum + customer_email: String + customer_name: CartAddressCustomerName + company: String + + street: [String] # Should this be an array? + city: String + region: CartAddressRegion +} + +type CartAddressRegion { + id: Int # Do we need to expose region ID? + label: String +} + +type CartAddressCustomerName { + first: String + last: String + prefix: String + suffix: String +} + +enum AdressTypeEnum { + SHIPPING + BILLING } -type SelectedShippingMethod { +type CheckoutShippingMethod { code: String! label: String! } From c5aaccbbd3a5c44b9effe71a6212f3392cd669b6 Mon Sep 17 00:00:00 2001 From: Alex Paliarush Date: Mon, 13 Aug 2018 17:46:21 -0500 Subject: [PATCH 12/22] Adjusted cart schema --- .../graph-ql/coverage/Cart.graphqls | 20 +++++++++++++++++++ .../coverage/CouponOperations.graphqls | 4 ++++ 2 files changed, 24 insertions(+) diff --git a/design-documents/graph-ql/coverage/Cart.graphqls b/design-documents/graph-ql/coverage/Cart.graphqls index 1d561609f..7312883d3 100644 --- a/design-documents/graph-ql/coverage/Cart.graphqls +++ b/design-documents/graph-ql/coverage/Cart.graphqls @@ -36,6 +36,26 @@ type CartAddress { street: [String] # Should this be an array? city: String region: CartAddressRegion + postcode: String + country: CartAddressCountry + telephone: String + fax: String + same_as_billing: Bool + + items_weight: Float + customer_notes: String + + gift_cards_amount_used: Money + applied_gift_cards: [CartGiftCard] +} + +type CartGiftCard { + code: String! +} + +type CartAddressCountry { + code: String + label: String } type CartAddressRegion { diff --git a/design-documents/graph-ql/coverage/CouponOperations.graphqls b/design-documents/graph-ql/coverage/CouponOperations.graphqls index 0f31072ae..186218fe7 100644 --- a/design-documents/graph-ql/coverage/CouponOperations.graphqls +++ b/design-documents/graph-ql/coverage/CouponOperations.graphqls @@ -16,6 +16,10 @@ type Cart { applied_coupon: AppliedCoupon } +type CartAddress { + applied_coupon: AppliedCoupon +} + type AppliedCoupon { # Wrapper allows for future extension of coupon info code: String! From c87159c4b6e6201a32b85701356df8f25b35e425 Mon Sep 17 00:00:00 2001 From: Alex Paliarush Date: Tue, 14 Aug 2018 14:06:24 -0500 Subject: [PATCH 13/22] Proposal to change resolver interface return type --- .../framework/resolver-output-type.md | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 design-documents/graph-ql/framework/resolver-output-type.md diff --git a/design-documents/graph-ql/framework/resolver-output-type.md b/design-documents/graph-ql/framework/resolver-output-type.md new file mode 100644 index 000000000..173d1e1a4 --- /dev/null +++ b/design-documents/graph-ql/framework/resolver-output-type.md @@ -0,0 +1,49 @@ +**Problem statement** + +All resolvers in Magento must implement Resolver interface, which in its turn forces the instance of `\GraphQL\Deferred` to be returned by each resolver. To be precise, Magento wrapper for *deferred*, `\Magento\Framework\GraphQl\Query\Resolver\Value`, must be returned. + +```php +/** + * Fetches the data from persistence models and format it according to the GraphQL schema. + * + * @param \Magento\Framework\GraphQl\Config\Element\Field $field + * @param $context + * @param ResolveInfo $info + * @param array|null $value + * @param array|null $args + * @throws \Exception + * @return Value + */ +public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null +) : Value; +``` + +The main reason for using *deferred* in GraphQL is to optimize performance by solving [N+1 problem](http://webonyx.github.io/graphql-php/data-fetching/#solving-n1-problem) where it exists. + +An example of valid *deferred* usage in Magento can be found in [`\Magento\CatalogGraphQl\Model\Resolver\Product::resolve`](https://github.com/magento/graphql-ce/blob/5a570b8b674b04eb36930aa73f92d1e789b8843a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product.php#L57). + +In all other cases, when there is no need to solve N+1 problem, we end up with boilerplate code like: +```php +$result = function () use ($data) { + return $data; +}; + +return $this->valueFactory->create($result); +``` + +Response type unification is one of the reasons why *deferred* was made the only possible return type for resolvers. However, after implementing more resolvers it became obvious that current design just adds unnecessary complexity to the resolver implementations. + +**Proposed solution** + +In addition to *deferred*, allow scalars and arrays of scalars as return types for `\Magento\CatalogGraphQl\Model\Resolver\Product::resolve`. +Proposed solution is backward compatible. + +**Action items** + +1. Modify existing resolvers, which have boilerplate code and do not require solving N+1 problem +1. Document the decision and make sure that all new resolvers return *deferred* only when necessary From f2ca044cac2c1d2249627f748874b17b987b09d4 Mon Sep 17 00:00:00 2001 From: Alex Paliarush Date: Tue, 14 Aug 2018 14:21:53 -0500 Subject: [PATCH 14/22] Proposal to change resolver interface return type --- .../framework/resolver-output-type.md | 36 ++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/design-documents/graph-ql/framework/resolver-output-type.md b/design-documents/graph-ql/framework/resolver-output-type.md index 173d1e1a4..151f7e02a 100644 --- a/design-documents/graph-ql/framework/resolver-output-type.md +++ b/design-documents/graph-ql/framework/resolver-output-type.md @@ -41,9 +41,43 @@ Response type unification is one of the reasons why *deferred* was made the only **Proposed solution** In addition to *deferred*, allow scalars and arrays of scalars as return types for `\Magento\CatalogGraphQl\Model\Resolver\Product::resolve`. -Proposed solution is backward compatible. + +It will also be easier to customize resolver with plugins, if it returns array/scalar instead of `deferred` object. **Action items** 1. Modify existing resolvers, which have boilerplate code and do not require solving N+1 problem + + Change + ```php + public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ) : Value; + ``` + to + ```php + public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ); + ``` + Also, replace boilerplate code in resolvers + ```php + $result = function () use ($data) { + return $data; + }; + + return $this->valueFactory->create($result); + ``` + with + ```php + return $data; + ``` 1. Document the decision and make sure that all new resolvers return *deferred* only when necessary From 10ce77ebf732ad25fa4bba63a8bd426496429aef Mon Sep 17 00:00:00 2001 From: Alex Paliarush Date: Wed, 15 Aug 2018 14:57:44 -0500 Subject: [PATCH 15/22] Adjusted cart schema --- .../graph-ql/coverage/Cart.graphqls | 40 ++++++++++++++----- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/design-documents/graph-ql/coverage/Cart.graphqls b/design-documents/graph-ql/coverage/Cart.graphqls index 7312883d3..3a63df062 100644 --- a/design-documents/graph-ql/coverage/Cart.graphqls +++ b/design-documents/graph-ql/coverage/Cart.graphqls @@ -13,10 +13,38 @@ type CartQueryOutput { type Cart { id: String! addresses: [CartAddress] + line_items_count: Int! items_quantity: Float! + selected_payment_method: CheckoutPaymentMethod available_payment_methods: [CheckoutPaymentMethod]! + + customer: CheckoutCustomerInfo + + gift_cards_amount_used: Money + applied_gift_cards: [CartGiftCard] + + is_multishipping: Bool! + is_virtual: Bool! +} + +type CheckoutCustomer { + is_guest: Bool! + email: String! + prefix: String + first_name: String! + last_name: String! + middle_name: String + suffix: String + gender: GenderEnum + date_of_birth: String + vat_number: String # Do we need it at all on storefront? Do we need more details +} + +enum GenderEnum { + MALE + FEMALE } type CheckoutPaymentMethod { @@ -29,8 +57,9 @@ type CartAddress { available_shipping_methods: [CheckoutShippingMethod]! address_type: AdressTypeEnum - customer_email: String - customer_name: CartAddressCustomerName + + customer: CheckoutCustomer + company: String street: [String] # Should this be an array? @@ -63,13 +92,6 @@ type CartAddressRegion { label: String } -type CartAddressCustomerName { - first: String - last: String - prefix: String - suffix: String -} - enum AdressTypeEnum { SHIPPING BILLING From 5cc770fb6ae046c3101bb51259dc8785c58cc236 Mon Sep 17 00:00:00 2001 From: Alex Paliarush Date: Thu, 16 Aug 2018 09:23:59 -0500 Subject: [PATCH 16/22] Adjusted cart schema --- .../graph-ql/coverage/Cart.graphqls | 13 +- .../coverage/MergedCartSchema.graphqls | 441 ++++++++++++++++++ .../AddGiftCardProductToCart.graphqls | 7 +- 3 files changed, 456 insertions(+), 5 deletions(-) create mode 100644 design-documents/graph-ql/coverage/MergedCartSchema.graphqls diff --git a/design-documents/graph-ql/coverage/Cart.graphqls b/design-documents/graph-ql/coverage/Cart.graphqls index 3a63df062..cc59222e2 100644 --- a/design-documents/graph-ql/coverage/Cart.graphqls +++ b/design-documents/graph-ql/coverage/Cart.graphqls @@ -21,16 +21,17 @@ type Cart { available_payment_methods: [CheckoutPaymentMethod]! customer: CheckoutCustomerInfo + customer_notes: String gift_cards_amount_used: Money applied_gift_cards: [CartGiftCard] - is_multishipping: Bool! - is_virtual: Bool! + is_multishipping: Boolean! + is_virtual: Boolean! } type CheckoutCustomer { - is_guest: Bool! + is_guest: Boolean! email: String! prefix: String first_name: String! @@ -50,6 +51,7 @@ enum GenderEnum { type CheckoutPaymentMethod { code: String! label: String! + # Do we need more info from quote_payment DB table? } type CartAddress { @@ -69,13 +71,16 @@ type CartAddress { country: CartAddressCountry telephone: String fax: String - same_as_billing: Bool + same_as_billing: Boolean items_weight: Float customer_notes: String gift_cards_amount_used: Money applied_gift_cards: [CartGiftCard] + + save_in_address_book: Boolean! + free_shipping: Boolean! # Do we need this field? Should it be moved to CheckoutShippingMethod type? } type CartGiftCard { diff --git a/design-documents/graph-ql/coverage/MergedCartSchema.graphqls b/design-documents/graph-ql/coverage/MergedCartSchema.graphqls new file mode 100644 index 000000000..de303b0c2 --- /dev/null +++ b/design-documents/graph-ql/coverage/MergedCartSchema.graphqls @@ -0,0 +1,441 @@ +type Query { + cart(input: CartQueryInput): CartQueryOutput +} + +type Mutation { + applyCouponToCart(input: ApplyCouponToCartInput): ApplyCouponToCartOutput + removeCouponFromCart(input: RemoveCouponFromCartInput): RemoveCouponFromCartOutput + addBundleProductsToCart(input: AddBundleProductsToCartInput): AddBundleProductsToCartOutput + addConfigurableProductsToCart(input: AddConfigurableProductsToCartInput): AddConfigurableProductsToCartOutput + addDownloadableProductsToCart(input: AddDownloadableProductsToCartInput): AddDownloadableProductsToCartOutput + addGiftCardProductsToCart(input: AddGiftCardProductsToCartInput): AddGiftCardProductsToCartOutput + addGroupedProductsToCart(input: AddGroupedProductsToCartInput): AddGroupedProductsToCartOutput + addSimpleProductsToCart(input: AddSimpleProductsToCartInput): AddSimpleProductsToCartOutput + # for now this mutation is identical to addSimpleProductsToCart and exists as a syntax sugar. Also it allows product type based customizations + addVirtualProductsToCart(input: AddVirtualProductsToCartInput): AddVirtualProductsToCartOutput +} + +input CartQueryInput { + cart_id: String! +} + +type CartQueryOutput { + cart: Cart +} + +type Cart { + id: String! + addresses: [CartAddress] + + line_items_count: Int! + items_quantity: Float! + + selected_payment_method: CheckoutPaymentMethod + available_payment_methods: [CheckoutPaymentMethod]! + + customer: CheckoutCustomer + custome_notes: String + + gift_cards_amount_used: Money + applied_gift_cards: [CartGiftCard] + + is_multishipping: Boolean! + is_virtual: Boolean! +} + +type CheckoutCustomer { + is_guest: Boolean! + email: String! + prefix: String + first_name: String! + last_name: String! + middle_name: String + suffix: String + gender: GenderEnum + date_of_birth: String + vat_number: String # Do we need it at all on storefront? Do we need more details +} + +enum GenderEnum { + MALE + FEMALE +} + +type CheckoutPaymentMethod { + code: String! + label: String! + # Do we need more info from quote_payment DB table? +} + +type CartAddress { + selected_shipping_method: CheckoutShippingMethod + available_shipping_methods: [CheckoutShippingMethod]! + + address_type: AdressTypeEnum + + customer: CheckoutCustomer + + company: String + + street: [String] # Should this be an array? + city: String + region: CartAddressRegion + postcode: String + country: CartAddressCountry + telephone: String + fax: String + same_as_billing: Boolean + + items_weight: Float + customer_notes: String + + gift_cards_amount_used: Money + applied_gift_cards: [CartGiftCard] + + save_in_address_book: Boolean! + free_shipping: Boolean! # Do we need this field? Should it be moved to CheckoutShippingMethod type? +} + +type CartGiftCard { + code: String! +} + +type CartAddressCountry { + code: String + label: String +} + +type CartAddressRegion { + id: Int # Do we need to expose region ID? + label: String +} + +enum AdressTypeEnum { + SHIPPING + BILLING +} + +type CheckoutShippingMethod { + code: String! + label: String! +} + + +interface CartItemInterface { + prices: CartItemPrices +} + +type Cart { + prices: CartPrices +} + +type CartAddress { + prices: CartAddressPrices + # additional fields will be added later +} + +interface CartPricesInterface { + grand_total: Money + + # price display settings should be requested via store config query + subtotal_including_tax: Money + subtotal_excluding_tax: Money + + subtotal_with_discount_excluding_tax: Money + discount_tax_compensation: Money #Should we have subtotal with discount including tax instead, is it different from grand_total? + + applied_taxes: [CartTaxItem]! # Should include regular taxes and WEEE taxes + applied_discounts: [CartDiscountItem]! +} + +type CartItemPrices implements CartPricesInterface { + price_including_tax: Money + price_excluding_tax: Money + + custom_price: Money +} + +type CartAddressPrices implements CartPricesInterface { + + shipping_including_tax: Money + shipping_excluding_tax: Money + + shipping_discount: Money # Do we need shipping_with_discount_including_tax? + shipping_discount_tax_compensation: Money +} + +type CartPrices implements CartPricesInterface { +} + +type CartTaxItem { + amount: Money! + label: String! +} + +type CartDiscountItem { + amount: Money! + label: String! +} + +input ApplyCouponToCartInput { + cart_id: String! + coupon_code: String! +} + +type ApplyCouponToCartOutput { + cart: Cart! +} + +type Cart { + applied_coupon: AppliedCoupon +} + +type CartAddress { + applied_coupon: AppliedCoupon +} + +type AppliedCoupon { + # Wrapper allows for future extension of coupon info + code: String! +} + +input RemoveCouponFromCartInput { + cart_id: String! +} + +type RemoveCouponFromCartOutput { + cart: Cart +} + +input AddBundleProductsToCartInput { + cart_id: String! + cartItems: [BundleProductCartItemInput!]! +} + +input BundleProductCartItemInput { + sku: String! + quantity: Float! + bundle_options:[BundleOptionInput!]! + customizable_options:[CustomizableOptionInput!] +} + +input BundleOptionInput { + id: Int! + quantity: Float! + value: [String!]! +} + +type AddBundleProductsToCartOutput { + cart: Cart! +} + +type BundleCartItem implements CartItemInterface { + customizable_options: [SelectedCustomizableOption]! + bundle_options: [SelectedBundleOption!]! +} + +type SelectedBundleOption { + id: Int! + label: String! + type: String! + # No quantity here even though it is set on option level in the input + values: [SelectedBundleOptionValue!]! + sort_order: Int! +} + +type SelectedBundleOptionValue { + id: Int! + label: String! + quantity: Float! # Quantity is displayed on option value level, while is set on option level + price: CartItemSelectedOptionValuePrice! + sort_order: Int! +} + +input AddConfigurableProductsToCartInput { + cart_id: String! + cartItems: [ConfigurableProductCartItemInput!]! +} + +input ConfigurableProductCartItemInput { + sku: String! + quantity: Float! + configurable_options:[ConfigurableOptionInput!]! + customizable_options:[CustomizableOptionInput!] +} + +input ConfigurableOptionInput { + id: Int! + value: Int! +} + +type AddConfigurableProductsToCartOutput { + cart: Cart! +} + +type ConfigurableCartItem implements CartItemInterface { + customizable_options: [SelectedCustomizableOption]! + configurable_options: [SelectedConfigurableOption!]! +} + +type SelectedConfigurableOption { + id: Int! + option_label: String! + value_id: Int! + value_label: String! +} + +input AddDownloadableProductsToCartInput { + cart_id: String! + cartItems: [DownloadableProductCartItemInput!]! +} + +input DownloadableProductCartItemInput { + sku: String! + quantity: Int! + downloadable_links: [DownloadableLinksInput!] + customizable_options:[CustomizableOptionInput!] +} + +input DownloadableLinksInput { + id: [Int!]! +} + +type AddDownloadableProductsToCartOutput { + cart: Cart! +} + +type DownloadableCartItem implements CartItemInterface { + links_label: String! + links: [DownloadableCartItemLink!]! + configurable_options: [SelectedConfigurableOption!]! +} + +type DownloadableCartItemLink { + id: Int! + label: String! +} + +input AddGiftCardProductsToCartInput { + cart_id: String! + cartItems: [GiftCardProductCartItemInput!]! +} + +input GiftCardProductCartItemInput { + sku: String! + quantity: Float! + sender_name: String! + recepient_name: String! + amount: Money! + message: String + customizable_options:[CustomizableOptionInput!] +} + +type AddGiftCardProductsToCartOutput { + cart: Cart! +} + +type GiftCardCartItem implements CartItemInterface { + sender_name: String! + recepient_name: String! + amount: Money! + message: String + customizable_options: [SelectedCustomizableOption]! +} + +input AddGroupedProductsToCartInput { + cart_id: String! + cartItems: [GroupedProductCartItemInput!]! +} + +input GroupedProductCartItemInput { + sku: String! + quantity: Float! + # the difference from simple products is that grouped products do not support customizable options +} + +type AddGroupedProductsToCartOutput { + cart: Cart! +} + +input AddSimpleProductsToCartInput { + cart_id: String! + cartItems: [SimpleProductCartItemInput!]! +} + +input SimpleProductCartItemInput { + sku: String! + quantity: Float! + customizable_options:[CustomizableOptionInput!] +} + +input CustomizableOptionInput { + id: Int! + value: String! +} + +type AddSimpleProductsToCartOutput { + cart: Cart! +} + +type Cart { + items: [CartItemInterface] +} + +interface CartItemInterface @typeResolver(class: "Magento\\CatalogCheckoutGraphQl\\Model\\CartItemInterfaceTypeResolverComposite") { + id: Int! + qty: Float! + product: ProductInterface! +} + +type SimpleCartItem implements CartItemInterface { + customizable_options: [SelectedCustomizableOption] +} + +type SelectedCustomizableOption { + id: Int! + label: String! + type: String! + values: [SelectedCustomizableOptionValue!]! + sort_order: Int! +} + +type SelectedCustomizableOptionValue { + id: Int + label: String! + price: CartItemSelectedOptionValuePrice! + sort_order: Int! +} + +type CartItemSelectedOptionValuePrice { + value: Float! + units: String! + type: PriceTypeEnum! +} + +input AddVirtualProductsToCartInput { + cart_id: String! + cartItems: [VirtualProductCartItemInput!]! +} + +input VirtualProductCartItemInput { + sku: String! + quantity: Float! + customizable_options:[CustomizableOptionInput!] +} + +type AddVirtualProductsToCartOutput { + cart: Cart! +} + +# Custom cart item type can be used to customize rendering when there are no physical producs available, e.g. skip shipping +type VirtualCartItem implements CartItemInterface { + customizable_options: [SelectedCustomizableOption] +} + + + + + + + + diff --git a/design-documents/graph-ql/coverage/add-items-to-cart/AddGiftCardProductToCart.graphqls b/design-documents/graph-ql/coverage/add-items-to-cart/AddGiftCardProductToCart.graphqls index e8e408e20..d13d1a37d 100644 --- a/design-documents/graph-ql/coverage/add-items-to-cart/AddGiftCardProductToCart.graphqls +++ b/design-documents/graph-ql/coverage/add-items-to-cart/AddGiftCardProductToCart.graphqls @@ -12,11 +12,16 @@ input GiftCardProductCartItemInput { quantity: Float! sender_name: String! recepient_name: String! - amount: Money! + amount: MoneyInput! # Do we need complex type here or just Float? message: String customizable_options:[CustomizableOptionInput!] } +input MoneyInput { + value: Float @doc(description: "A number expressing a monetary value") + currency: CurrencyEnum @doc(description: "A three-letter currency code, such as USD or EUR") +} + type AddGiftCardProductsToCartOutput { cart: Cart! } From b0bc80eb7a438628eda6ace66b71995b1a6c863f Mon Sep 17 00:00:00 2001 From: Alex Paliarush Date: Fri, 17 Aug 2018 17:30:18 -0500 Subject: [PATCH 17/22] Adjusted cart schema - Improved extensibility for cart item mutations - Added cart item removal and update schemas --- .../coverage/MergedCartSchema.graphqls | 441 ------------------ .../AddBundleProductToCart.graphqls | 15 +- .../AddConfigurableProductToCart.graphqls | 15 +- .../AddDownloadableProductToCart.graphqls | 15 +- .../AddGiftCardProductToCart.graphqls | 18 +- .../AddGroupedProductToCart.graphqls | 13 +- .../AddSimpleProductToCart.graphqls | 24 +- .../AddVirtualProductToCart.graphqls | 14 +- .../RemoveItemFromCart.graphqls | 12 + 9 files changed, 112 insertions(+), 455 deletions(-) delete mode 100644 design-documents/graph-ql/coverage/MergedCartSchema.graphqls create mode 100644 design-documents/graph-ql/coverage/add-items-to-cart/RemoveItemFromCart.graphqls diff --git a/design-documents/graph-ql/coverage/MergedCartSchema.graphqls b/design-documents/graph-ql/coverage/MergedCartSchema.graphqls deleted file mode 100644 index de303b0c2..000000000 --- a/design-documents/graph-ql/coverage/MergedCartSchema.graphqls +++ /dev/null @@ -1,441 +0,0 @@ -type Query { - cart(input: CartQueryInput): CartQueryOutput -} - -type Mutation { - applyCouponToCart(input: ApplyCouponToCartInput): ApplyCouponToCartOutput - removeCouponFromCart(input: RemoveCouponFromCartInput): RemoveCouponFromCartOutput - addBundleProductsToCart(input: AddBundleProductsToCartInput): AddBundleProductsToCartOutput - addConfigurableProductsToCart(input: AddConfigurableProductsToCartInput): AddConfigurableProductsToCartOutput - addDownloadableProductsToCart(input: AddDownloadableProductsToCartInput): AddDownloadableProductsToCartOutput - addGiftCardProductsToCart(input: AddGiftCardProductsToCartInput): AddGiftCardProductsToCartOutput - addGroupedProductsToCart(input: AddGroupedProductsToCartInput): AddGroupedProductsToCartOutput - addSimpleProductsToCart(input: AddSimpleProductsToCartInput): AddSimpleProductsToCartOutput - # for now this mutation is identical to addSimpleProductsToCart and exists as a syntax sugar. Also it allows product type based customizations - addVirtualProductsToCart(input: AddVirtualProductsToCartInput): AddVirtualProductsToCartOutput -} - -input CartQueryInput { - cart_id: String! -} - -type CartQueryOutput { - cart: Cart -} - -type Cart { - id: String! - addresses: [CartAddress] - - line_items_count: Int! - items_quantity: Float! - - selected_payment_method: CheckoutPaymentMethod - available_payment_methods: [CheckoutPaymentMethod]! - - customer: CheckoutCustomer - custome_notes: String - - gift_cards_amount_used: Money - applied_gift_cards: [CartGiftCard] - - is_multishipping: Boolean! - is_virtual: Boolean! -} - -type CheckoutCustomer { - is_guest: Boolean! - email: String! - prefix: String - first_name: String! - last_name: String! - middle_name: String - suffix: String - gender: GenderEnum - date_of_birth: String - vat_number: String # Do we need it at all on storefront? Do we need more details -} - -enum GenderEnum { - MALE - FEMALE -} - -type CheckoutPaymentMethod { - code: String! - label: String! - # Do we need more info from quote_payment DB table? -} - -type CartAddress { - selected_shipping_method: CheckoutShippingMethod - available_shipping_methods: [CheckoutShippingMethod]! - - address_type: AdressTypeEnum - - customer: CheckoutCustomer - - company: String - - street: [String] # Should this be an array? - city: String - region: CartAddressRegion - postcode: String - country: CartAddressCountry - telephone: String - fax: String - same_as_billing: Boolean - - items_weight: Float - customer_notes: String - - gift_cards_amount_used: Money - applied_gift_cards: [CartGiftCard] - - save_in_address_book: Boolean! - free_shipping: Boolean! # Do we need this field? Should it be moved to CheckoutShippingMethod type? -} - -type CartGiftCard { - code: String! -} - -type CartAddressCountry { - code: String - label: String -} - -type CartAddressRegion { - id: Int # Do we need to expose region ID? - label: String -} - -enum AdressTypeEnum { - SHIPPING - BILLING -} - -type CheckoutShippingMethod { - code: String! - label: String! -} - - -interface CartItemInterface { - prices: CartItemPrices -} - -type Cart { - prices: CartPrices -} - -type CartAddress { - prices: CartAddressPrices - # additional fields will be added later -} - -interface CartPricesInterface { - grand_total: Money - - # price display settings should be requested via store config query - subtotal_including_tax: Money - subtotal_excluding_tax: Money - - subtotal_with_discount_excluding_tax: Money - discount_tax_compensation: Money #Should we have subtotal with discount including tax instead, is it different from grand_total? - - applied_taxes: [CartTaxItem]! # Should include regular taxes and WEEE taxes - applied_discounts: [CartDiscountItem]! -} - -type CartItemPrices implements CartPricesInterface { - price_including_tax: Money - price_excluding_tax: Money - - custom_price: Money -} - -type CartAddressPrices implements CartPricesInterface { - - shipping_including_tax: Money - shipping_excluding_tax: Money - - shipping_discount: Money # Do we need shipping_with_discount_including_tax? - shipping_discount_tax_compensation: Money -} - -type CartPrices implements CartPricesInterface { -} - -type CartTaxItem { - amount: Money! - label: String! -} - -type CartDiscountItem { - amount: Money! - label: String! -} - -input ApplyCouponToCartInput { - cart_id: String! - coupon_code: String! -} - -type ApplyCouponToCartOutput { - cart: Cart! -} - -type Cart { - applied_coupon: AppliedCoupon -} - -type CartAddress { - applied_coupon: AppliedCoupon -} - -type AppliedCoupon { - # Wrapper allows for future extension of coupon info - code: String! -} - -input RemoveCouponFromCartInput { - cart_id: String! -} - -type RemoveCouponFromCartOutput { - cart: Cart -} - -input AddBundleProductsToCartInput { - cart_id: String! - cartItems: [BundleProductCartItemInput!]! -} - -input BundleProductCartItemInput { - sku: String! - quantity: Float! - bundle_options:[BundleOptionInput!]! - customizable_options:[CustomizableOptionInput!] -} - -input BundleOptionInput { - id: Int! - quantity: Float! - value: [String!]! -} - -type AddBundleProductsToCartOutput { - cart: Cart! -} - -type BundleCartItem implements CartItemInterface { - customizable_options: [SelectedCustomizableOption]! - bundle_options: [SelectedBundleOption!]! -} - -type SelectedBundleOption { - id: Int! - label: String! - type: String! - # No quantity here even though it is set on option level in the input - values: [SelectedBundleOptionValue!]! - sort_order: Int! -} - -type SelectedBundleOptionValue { - id: Int! - label: String! - quantity: Float! # Quantity is displayed on option value level, while is set on option level - price: CartItemSelectedOptionValuePrice! - sort_order: Int! -} - -input AddConfigurableProductsToCartInput { - cart_id: String! - cartItems: [ConfigurableProductCartItemInput!]! -} - -input ConfigurableProductCartItemInput { - sku: String! - quantity: Float! - configurable_options:[ConfigurableOptionInput!]! - customizable_options:[CustomizableOptionInput!] -} - -input ConfigurableOptionInput { - id: Int! - value: Int! -} - -type AddConfigurableProductsToCartOutput { - cart: Cart! -} - -type ConfigurableCartItem implements CartItemInterface { - customizable_options: [SelectedCustomizableOption]! - configurable_options: [SelectedConfigurableOption!]! -} - -type SelectedConfigurableOption { - id: Int! - option_label: String! - value_id: Int! - value_label: String! -} - -input AddDownloadableProductsToCartInput { - cart_id: String! - cartItems: [DownloadableProductCartItemInput!]! -} - -input DownloadableProductCartItemInput { - sku: String! - quantity: Int! - downloadable_links: [DownloadableLinksInput!] - customizable_options:[CustomizableOptionInput!] -} - -input DownloadableLinksInput { - id: [Int!]! -} - -type AddDownloadableProductsToCartOutput { - cart: Cart! -} - -type DownloadableCartItem implements CartItemInterface { - links_label: String! - links: [DownloadableCartItemLink!]! - configurable_options: [SelectedConfigurableOption!]! -} - -type DownloadableCartItemLink { - id: Int! - label: String! -} - -input AddGiftCardProductsToCartInput { - cart_id: String! - cartItems: [GiftCardProductCartItemInput!]! -} - -input GiftCardProductCartItemInput { - sku: String! - quantity: Float! - sender_name: String! - recepient_name: String! - amount: Money! - message: String - customizable_options:[CustomizableOptionInput!] -} - -type AddGiftCardProductsToCartOutput { - cart: Cart! -} - -type GiftCardCartItem implements CartItemInterface { - sender_name: String! - recepient_name: String! - amount: Money! - message: String - customizable_options: [SelectedCustomizableOption]! -} - -input AddGroupedProductsToCartInput { - cart_id: String! - cartItems: [GroupedProductCartItemInput!]! -} - -input GroupedProductCartItemInput { - sku: String! - quantity: Float! - # the difference from simple products is that grouped products do not support customizable options -} - -type AddGroupedProductsToCartOutput { - cart: Cart! -} - -input AddSimpleProductsToCartInput { - cart_id: String! - cartItems: [SimpleProductCartItemInput!]! -} - -input SimpleProductCartItemInput { - sku: String! - quantity: Float! - customizable_options:[CustomizableOptionInput!] -} - -input CustomizableOptionInput { - id: Int! - value: String! -} - -type AddSimpleProductsToCartOutput { - cart: Cart! -} - -type Cart { - items: [CartItemInterface] -} - -interface CartItemInterface @typeResolver(class: "Magento\\CatalogCheckoutGraphQl\\Model\\CartItemInterfaceTypeResolverComposite") { - id: Int! - qty: Float! - product: ProductInterface! -} - -type SimpleCartItem implements CartItemInterface { - customizable_options: [SelectedCustomizableOption] -} - -type SelectedCustomizableOption { - id: Int! - label: String! - type: String! - values: [SelectedCustomizableOptionValue!]! - sort_order: Int! -} - -type SelectedCustomizableOptionValue { - id: Int - label: String! - price: CartItemSelectedOptionValuePrice! - sort_order: Int! -} - -type CartItemSelectedOptionValuePrice { - value: Float! - units: String! - type: PriceTypeEnum! -} - -input AddVirtualProductsToCartInput { - cart_id: String! - cartItems: [VirtualProductCartItemInput!]! -} - -input VirtualProductCartItemInput { - sku: String! - quantity: Float! - customizable_options:[CustomizableOptionInput!] -} - -type AddVirtualProductsToCartOutput { - cart: Cart! -} - -# Custom cart item type can be used to customize rendering when there are no physical producs available, e.g. skip shipping -type VirtualCartItem implements CartItemInterface { - customizable_options: [SelectedCustomizableOption] -} - - - - - - - - diff --git a/design-documents/graph-ql/coverage/add-items-to-cart/AddBundleProductToCart.graphqls b/design-documents/graph-ql/coverage/add-items-to-cart/AddBundleProductToCart.graphqls index 674dd0806..e92f12223 100644 --- a/design-documents/graph-ql/coverage/add-items-to-cart/AddBundleProductToCart.graphqls +++ b/design-documents/graph-ql/coverage/add-items-to-cart/AddBundleProductToCart.graphqls @@ -1,5 +1,17 @@ type Mutation { addBundleProductsToCart(input: AddBundleProductsToCartInput): AddBundleProductsToCartOutput + updateBundleProductsInCart(input: UpdateBundleProductsInCartInput): UpdateBundleProductsInCartOutput +} + +input UpdateBundleProductsInCartInput { + cart_id: String! + cartItems: [UpdateBundleProductCartItemInput!]! +} + +input UpdateBundleProductCartItemInput { + details: UpdateCartItemDetailsInput! + bundle_options:[BundleOptionInput!] + customizable_options:[CustomizableOptionInput] } input AddBundleProductsToCartInput { @@ -8,8 +20,7 @@ input AddBundleProductsToCartInput { } input BundleProductCartItemInput { - sku: String! - quantity: Float! + details: CartItemDetailsInput! bundle_options:[BundleOptionInput!]! customizable_options:[CustomizableOptionInput!] } diff --git a/design-documents/graph-ql/coverage/add-items-to-cart/AddConfigurableProductToCart.graphqls b/design-documents/graph-ql/coverage/add-items-to-cart/AddConfigurableProductToCart.graphqls index e1e4af9d9..f98875952 100644 --- a/design-documents/graph-ql/coverage/add-items-to-cart/AddConfigurableProductToCart.graphqls +++ b/design-documents/graph-ql/coverage/add-items-to-cart/AddConfigurableProductToCart.graphqls @@ -1,5 +1,17 @@ type Mutation { addConfigurableProductsToCart(input: AddConfigurableProductsToCartInput): AddConfigurableProductsToCartOutput + updateConfigurableProductsInCart(input: UpdateConfigurableProductsInCartInput): UpdateConfigurableProductsInCartOutput +} + +input UpdateConfigurableProductsInCartInput { + cart_id: String! + cartItems: [UpdateConfigurableProductCartItemInput!]! +} + +input UpdateConfigurableProductCartItemInput { + details: UpdateCartItemDetailsInput! + configurable_options:[ConfigurableOptionInput!] + customizable_options:[CustomizableOptionInput] } input AddConfigurableProductsToCartInput { @@ -8,8 +20,7 @@ input AddConfigurableProductsToCartInput { } input ConfigurableProductCartItemInput { - sku: String! - quantity: Float! + details: CartItemDetailsInput! configurable_options:[ConfigurableOptionInput!]! customizable_options:[CustomizableOptionInput!] } diff --git a/design-documents/graph-ql/coverage/add-items-to-cart/AddDownloadableProductToCart.graphqls b/design-documents/graph-ql/coverage/add-items-to-cart/AddDownloadableProductToCart.graphqls index 6e36938ec..5ac44c289 100644 --- a/design-documents/graph-ql/coverage/add-items-to-cart/AddDownloadableProductToCart.graphqls +++ b/design-documents/graph-ql/coverage/add-items-to-cart/AddDownloadableProductToCart.graphqls @@ -1,5 +1,17 @@ type Mutation { addDownloadableProductsToCart(input: AddDownloadableProductsToCartInput): AddDownloadableProductsToCartOutput + updateDownloadableProductsInCart(input: UpdateDownloadableProductsInCartInput): UpdateDownloadableProductsInCartOutput +} + +input UpdateDownloadableProductsInCartInput { + cart_id: String! + cartItems: [UpdateDownloadableProductCartItemInput!]! +} + +input UpdateDownloadableProductCartItemInput { + details: UpdateCartItemDetailsInput! + downloadable_links: [DownloadableLinksInput] + customizable_options:[CustomizableOptionInput] } input AddDownloadableProductsToCartInput { @@ -8,8 +20,7 @@ input AddDownloadableProductsToCartInput { } input DownloadableProductCartItemInput { - sku: String! - quantity: Int! + details: CartItemDetailsInput! downloadable_links: [DownloadableLinksInput!] customizable_options:[CustomizableOptionInput!] } diff --git a/design-documents/graph-ql/coverage/add-items-to-cart/AddGiftCardProductToCart.graphqls b/design-documents/graph-ql/coverage/add-items-to-cart/AddGiftCardProductToCart.graphqls index d13d1a37d..92ae1ddb6 100644 --- a/design-documents/graph-ql/coverage/add-items-to-cart/AddGiftCardProductToCart.graphqls +++ b/design-documents/graph-ql/coverage/add-items-to-cart/AddGiftCardProductToCart.graphqls @@ -1,5 +1,20 @@ type Mutation { addGiftCardProductsToCart(input: AddGiftCardProductsToCartInput): AddGiftCardProductsToCartOutput + updateGiftCardProductsInCart(input: UpdateGiftCardProductsInCartInput): UpdateGiftCardProductsInCartOutput +} + +input UpdateGiftCardProductsInCartInput { + cart_id: String! + cartItems: [UpdateGiftCardProductCartItemInput!]! +} + +input UpdateGiftCardProductCartItemInput { + details: UpdateCartItemDetailsInput! + sender_name: String + recepient_name: String + amount: MoneyInput # Do we need complex type here or just Float? + message: String + customizable_options:[CustomizableOptionInput] } input AddGiftCardProductsToCartInput { @@ -8,8 +23,7 @@ input AddGiftCardProductsToCartInput { } input GiftCardProductCartItemInput { - sku: String! - quantity: Float! + details: CartItemDetailsInput! sender_name: String! recepient_name: String! amount: MoneyInput! # Do we need complex type here or just Float? diff --git a/design-documents/graph-ql/coverage/add-items-to-cart/AddGroupedProductToCart.graphqls b/design-documents/graph-ql/coverage/add-items-to-cart/AddGroupedProductToCart.graphqls index 322a99a70..38f97184f 100644 --- a/design-documents/graph-ql/coverage/add-items-to-cart/AddGroupedProductToCart.graphqls +++ b/design-documents/graph-ql/coverage/add-items-to-cart/AddGroupedProductToCart.graphqls @@ -1,5 +1,15 @@ type Mutation { addGroupedProductsToCart(input: AddGroupedProductsToCartInput): AddGroupedProductsToCartOutput + updateGroupedProductsInCart(input: UpdateGroupedProductsInCartInput): UpdateGroupedProductsInCartOutput +} + +input UpdateGroupedProductsInCartInput { + cart_id: String! + cartItems: [UpdateGroupedProductCartItemInput!]! +} + +input UpdateGroupedProductCartItemInput { + details: UpdateCartItemDetailsInput! } input AddGroupedProductsToCartInput { @@ -8,8 +18,7 @@ input AddGroupedProductsToCartInput { } input GroupedProductCartItemInput { - sku: String! - quantity: Float! + details: CartItemDetailsInput! # the difference from simple products is that grouped products do not support customizable options } diff --git a/design-documents/graph-ql/coverage/add-items-to-cart/AddSimpleProductToCart.graphqls b/design-documents/graph-ql/coverage/add-items-to-cart/AddSimpleProductToCart.graphqls index 6738e8466..8a3d6963f 100644 --- a/design-documents/graph-ql/coverage/add-items-to-cart/AddSimpleProductToCart.graphqls +++ b/design-documents/graph-ql/coverage/add-items-to-cart/AddSimpleProductToCart.graphqls @@ -1,5 +1,16 @@ type Mutation { addSimpleProductsToCart(input: AddSimpleProductsToCartInput): AddSimpleProductsToCartOutput + updateSimpleProductsInCart(input: UpdateSimpleProductsInCartInput): UpdateSimpleProductsInCartOutput +} + +input UpdateSimpleProductsInCartInput { + cart_id: String! + cartItems: [UpdateSimpleProductCartItemInput!]! +} + +input UpdateSimpleProductCartItemInput { + details: UpdateCartItemDetailsInput! + customizable_options:[CustomizableOptionInput] } input AddSimpleProductsToCartInput { @@ -8,9 +19,18 @@ input AddSimpleProductsToCartInput { } input SimpleProductCartItemInput { + details: CartItemDetailsInput! + customizable_options:[CustomizableOptionInput!] +} + +input CartItemDetailsInput { sku: String! quantity: Float! - customizable_options:[CustomizableOptionInput!] +} + +input UpdateCartItemDetailsInput { + cart_item_id: String! + quantity: Float } input CustomizableOptionInput { @@ -27,7 +47,7 @@ type Cart { } interface CartItemInterface @typeResolver(class: "Magento\\CatalogCheckoutGraphQl\\Model\\CartItemInterfaceTypeResolverComposite") { - id: Int! + id: String! # Hashed cart item ID qty: Float! product: ProductInterface! } diff --git a/design-documents/graph-ql/coverage/add-items-to-cart/AddVirtualProductToCart.graphqls b/design-documents/graph-ql/coverage/add-items-to-cart/AddVirtualProductToCart.graphqls index 1316d95f9..76013ada1 100644 --- a/design-documents/graph-ql/coverage/add-items-to-cart/AddVirtualProductToCart.graphqls +++ b/design-documents/graph-ql/coverage/add-items-to-cart/AddVirtualProductToCart.graphqls @@ -1,6 +1,17 @@ type Mutation { # for now this mutation is identical to addSimpleProductsToCart and exists as a syntax sugar. Also it allows product type based customizations addVirtualProductsToCart(input: AddVirtualProductsToCartInput): AddVirtualProductsToCartOutput + updateVirtualProductsInCart(input: UpdateVirtualProductsInCartInput): UpdateVirtualProductsInCartOutput +} + +input UpdateVirtualProductsInCartInput { + cart_id: String! + cartItems: [UpdateVirtualProductCartItemInput!]! +} + +input UpdateVirtualProductCartItemInput { + details: UpdateCartItemDetailsInput! + customizable_options:[CustomizableOptionInput] } input AddVirtualProductsToCartInput { @@ -9,8 +20,7 @@ input AddVirtualProductsToCartInput { } input VirtualProductCartItemInput { - sku: String! - quantity: Float! + details: CartItemDetailsInput! customizable_options:[CustomizableOptionInput!] } diff --git a/design-documents/graph-ql/coverage/add-items-to-cart/RemoveItemFromCart.graphqls b/design-documents/graph-ql/coverage/add-items-to-cart/RemoveItemFromCart.graphqls new file mode 100644 index 000000000..f1039fc86 --- /dev/null +++ b/design-documents/graph-ql/coverage/add-items-to-cart/RemoveItemFromCart.graphqls @@ -0,0 +1,12 @@ +type Mutation { + removeItemFromCart(input: RemoveItemFromCartInput): RemoveItemFromCartOutput +} + +input RemoveItemFromCartInput { + cart_id: String! + cart_item_id: String! +} + +type RemoveItemFromCartOutput { + cart: Cart! +} From fa08ab1b8f436712415781903f482fe47b036ddf Mon Sep 17 00:00:00 2001 From: Alex Paliarush Date: Tue, 21 Aug 2018 10:52:37 -0500 Subject: [PATCH 18/22] Proposal for input-output extensibility --- ...ppers.md => input-optput-extensibility.md} | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) rename design-documents/graph-ql/coverage/{input-optput-wrappers.md => input-optput-extensibility.md} (61%) diff --git a/design-documents/graph-ql/coverage/input-optput-wrappers.md b/design-documents/graph-ql/coverage/input-optput-extensibility.md similarity index 61% rename from design-documents/graph-ql/coverage/input-optput-wrappers.md rename to design-documents/graph-ql/coverage/input-optput-extensibility.md index d2a4e929b..814cdd2c7 100644 --- a/design-documents/graph-ql/coverage/input-optput-wrappers.md +++ b/design-documents/graph-ql/coverage/input-optput-extensibility.md @@ -17,18 +17,16 @@ Such declaration has several issues: - It is not possible to evolve arguments list and output type of our API in the future. Even if we decide to do breaking changes, there is no way to deprecate existing arguments and output data first -**Proposed solution: Wrappers for arguments and output** +**Proposed solution: Wrappers for output and merger for arguments** -Wrappers for arguments and output type can solve extensibility and deprecation issues. +Wrappers for output type along with merging capabilities for arguments can solve extensibility and deprecation issues. ```$graphqls type Mutation { - generateCustomerToken(input: GenerateCustomerTokenInput!): GenerateCustomerTokenOutput! -} - -input GenerateCustomerTokenInput { - email: String! - password: String! + generateCustomerToken( + email: String!, + password: String! + ): GenerateCustomerTokenOutput! } type GenerateCustomerTokenOutput { @@ -36,11 +34,15 @@ type GenerateCustomerTokenOutput { } ``` -With such schema it is easy to extend the list of arguments. For example, if system integrator got a new requirement to enable multi-factor authentication, the schema can be extended from 3rd party module to support this requirement as follows. +With such schema it is possible to extend the list of arguments (not reduce, however). For example, if system integrator got a new requirement to enable multi-factor authentication, the schema can be extended from 3rd party module to support this requirement as follows. All arguments from different modules will be merged and the resulting schema will contain all of them. ```$graphqls -input GenerateCustomerTokenInput { - multi_factor_auth_token: String! +type Mutation { + generateCustomerToken( + email: String!, + password: String!, + multi_factor_auth_token: String! + ): GenerateCustomerTokenOutput! } ``` Additionally, plugin can be added for the `generateCustomerToken` mutation resolver to implement additional verification step. @@ -57,9 +59,5 @@ The `token_ttl` value can be populated via new resolver for this field or from t **Action items** -1. Modify schema for all existing queries and mutations +1. Modify schema for all existing queries and mutations to use wrappers as output types 1. Make sure that this requirement is documented and followed for the new GraphQL coverage - -**Open questions** - -1. What can be one about queries and mutations, which do not require arguments at the moment? We cannot create wrapper type without fields. Would be nice to enable extensibility for sich argument lists as well. From 5a831aa18ac4c45fd430361e4545b78310fd6a23 Mon Sep 17 00:00:00 2001 From: Alex Paliarush Date: Tue, 21 Aug 2018 11:01:46 -0500 Subject: [PATCH 19/22] Proposal for input-output extensibility --- design-documents/graph-ql/coverage/input-optput-extensibility.md | 1 + 1 file changed, 1 insertion(+) diff --git a/design-documents/graph-ql/coverage/input-optput-extensibility.md b/design-documents/graph-ql/coverage/input-optput-extensibility.md index 814cdd2c7..c86805e15 100644 --- a/design-documents/graph-ql/coverage/input-optput-extensibility.md +++ b/design-documents/graph-ql/coverage/input-optput-extensibility.md @@ -61,3 +61,4 @@ The `token_ttl` value can be populated via new resolver for this field or from t 1. Modify schema for all existing queries and mutations to use wrappers as output types 1. Make sure that this requirement is documented and followed for the new GraphQL coverage +1. Implement merging of arguments for the same query/mutation defined in different modules. GraphQL functional tests should be added From a940475eebb7093e8dce5b32e9e68315c4b95e68 Mon Sep 17 00:00:00 2001 From: Alex Paliarush Date: Fri, 7 Sep 2018 12:08:55 -0500 Subject: [PATCH 20/22] Adding configurable products to cart using variant SKU instead of an array of options --- .../AddConfigurableProductToCart.graphqls | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/design-documents/graph-ql/coverage/add-items-to-cart/AddConfigurableProductToCart.graphqls b/design-documents/graph-ql/coverage/add-items-to-cart/AddConfigurableProductToCart.graphqls index f98875952..7dc069af0 100644 --- a/design-documents/graph-ql/coverage/add-items-to-cart/AddConfigurableProductToCart.graphqls +++ b/design-documents/graph-ql/coverage/add-items-to-cart/AddConfigurableProductToCart.graphqls @@ -10,7 +10,7 @@ input UpdateConfigurableProductsInCartInput { input UpdateConfigurableProductCartItemInput { details: UpdateCartItemDetailsInput! - configurable_options:[ConfigurableOptionInput!] + variant_sku: String customizable_options:[CustomizableOptionInput] } @@ -21,15 +21,10 @@ input AddConfigurableProductsToCartInput { input ConfigurableProductCartItemInput { details: CartItemDetailsInput! - configurable_options:[ConfigurableOptionInput!]! + variant_sku: String! customizable_options:[CustomizableOptionInput!] } -input ConfigurableOptionInput { - id: Int! - value: Int! -} - type AddConfigurableProductsToCartOutput { cart: Cart! } From 299809a1922f5f93991c5a7c194d604541d5fef6 Mon Sep 17 00:00:00 2001 From: Alex Paliarush Date: Tue, 18 Sep 2018 17:00:07 -0500 Subject: [PATCH 21/22] Added schema for cart address operations --- .../graph-ql/coverage/Cart.graphqls | 52 +----------- .../coverage/CartAddressOperations.graphqls | 84 +++++++++++++++++++ 2 files changed, 85 insertions(+), 51 deletions(-) create mode 100644 design-documents/graph-ql/coverage/CartAddressOperations.graphqls diff --git a/design-documents/graph-ql/coverage/Cart.graphqls b/design-documents/graph-ql/coverage/Cart.graphqls index cc59222e2..cdef09714 100644 --- a/design-documents/graph-ql/coverage/Cart.graphqls +++ b/design-documents/graph-ql/coverage/Cart.graphqls @@ -12,7 +12,6 @@ type CartQueryOutput { type Cart { id: String! - addresses: [CartAddress] line_items_count: Int! items_quantity: Float! @@ -20,7 +19,7 @@ type Cart { selected_payment_method: CheckoutPaymentMethod available_payment_methods: [CheckoutPaymentMethod]! - customer: CheckoutCustomerInfo + customer: CheckoutCustomer customer_notes: String gift_cards_amount_used: Money @@ -54,55 +53,6 @@ type CheckoutPaymentMethod { # Do we need more info from quote_payment DB table? } -type CartAddress { - selected_shipping_method: CheckoutShippingMethod - available_shipping_methods: [CheckoutShippingMethod]! - - address_type: AdressTypeEnum - - customer: CheckoutCustomer - - company: String - - street: [String] # Should this be an array? - city: String - region: CartAddressRegion - postcode: String - country: CartAddressCountry - telephone: String - fax: String - same_as_billing: Boolean - - items_weight: Float - customer_notes: String - - gift_cards_amount_used: Money - applied_gift_cards: [CartGiftCard] - - save_in_address_book: Boolean! - free_shipping: Boolean! # Do we need this field? Should it be moved to CheckoutShippingMethod type? -} - type CartGiftCard { code: String! } - -type CartAddressCountry { - code: String - label: String -} - -type CartAddressRegion { - id: Int # Do we need to expose region ID? - label: String -} - -enum AdressTypeEnum { - SHIPPING - BILLING -} - -type CheckoutShippingMethod { - code: String! - label: String! -} diff --git a/design-documents/graph-ql/coverage/CartAddressOperations.graphqls b/design-documents/graph-ql/coverage/CartAddressOperations.graphqls new file mode 100644 index 000000000..788d119b8 --- /dev/null +++ b/design-documents/graph-ql/coverage/CartAddressOperations.graphqls @@ -0,0 +1,84 @@ +type Query { + addBillingAddressToCart(input: AddBillingAddressToCartInput): AddBillingAddressToCartOutput + addShippingAddressToCart(input: AddShippingAddressToCartInput): AddShippingAddressToCartOutput +} + +input AddBillingAddressToCartInput { + address_id: Int + address: CartAddressInput! +} + +input AddShippingAddressToCartInput { + address_id: Int # Can be provided in one-page checkout and is required for multi-shipping checkout + address: CartAddressInput + shipping_method_code: String # Should be on address level for multi-shipping checkout +} + +input CartAddressInput { + firstname: String! + lastname: String! + company: String + street: [String!]! + city: String! + region: String + postcode: String + country_code: String! + telephone: String! + fax: String + save_in_address_book: Boolean! +} + +type AddShippingAddressToCartOutput { + cart: Cart! +} + +type AddBillingAddressToCartOutput { + cart: Cart! +} + +type Cart { + addresses: [CartAddress]! +} + +type CartAddress { + firstname: String! + lastname: String! + company: String + street: [String!]! + city: String! + region: CartAddressRegion + postcode: String + country: CartAddressCountry! + telephone: String! + fax: String + address_type: AdressTypeEnum! + + selected_shipping_method: CheckoutShippingMethod + available_shipping_methods: [CheckoutShippingMethod]! + + items_weight: Float + customer_notes: String + gift_cards_amount_used: Money + applied_gift_cards: [CartGiftCard] +} + +type CartAddressCountry { + code: String + label: String +} + +type CartAddressRegion { + code: String + label: String +} + +enum AdressTypeEnum { + SHIPPING + BILLING +} + +type CheckoutShippingMethod { + code: String! + label: String! + free_shipping: Boolean! +} From 963509274b4e289e55b723d9d559e117b881483c Mon Sep 17 00:00:00 2001 From: Alex Paliarush Date: Wed, 19 Sep 2018 22:21:07 -0500 Subject: [PATCH 22/22] Iterated on schema for cart address operations --- .../graph-ql/coverage/Cart.graphqls | 3 +- .../coverage/CartAddressOperations.graphqls | 54 ++++++++++++++----- 2 files changed, 42 insertions(+), 15 deletions(-) diff --git a/design-documents/graph-ql/coverage/Cart.graphqls b/design-documents/graph-ql/coverage/Cart.graphqls index cdef09714..2cf44bac2 100644 --- a/design-documents/graph-ql/coverage/Cart.graphqls +++ b/design-documents/graph-ql/coverage/Cart.graphqls @@ -50,7 +50,8 @@ enum GenderEnum { type CheckoutPaymentMethod { code: String! label: String! - # Do we need more info from quote_payment DB table? + balance: Money + sort_order: Int } type CartGiftCard { diff --git a/design-documents/graph-ql/coverage/CartAddressOperations.graphqls b/design-documents/graph-ql/coverage/CartAddressOperations.graphqls index 788d119b8..6abb53aa1 100644 --- a/design-documents/graph-ql/coverage/CartAddressOperations.graphqls +++ b/design-documents/graph-ql/coverage/CartAddressOperations.graphqls @@ -1,17 +1,32 @@ type Query { - addBillingAddressToCart(input: AddBillingAddressToCartInput): AddBillingAddressToCartOutput - addShippingAddressToCart(input: AddShippingAddressToCartInput): AddShippingAddressToCartOutput + setBillingAddressOnCart(input: SetBillingAddressOnCartInput): SetBillingAddressOnCartOutput + setShippingAddressesOnCart(input: SetShippingAddressesOnCartInput): SetShippingAddressesOnCartOutput + setShippingMethodsOnCart(input: SetShippingMethodsOnCartInput): SetShippingMethodsOnCartOutput } -input AddBillingAddressToCartInput { - address_id: Int - address: CartAddressInput! +input SetShippingMethodsOnCartInput { + shipping_methods: [ShippingMethodForAddressInput!]! } -input AddShippingAddressToCartInput { - address_id: Int # Can be provided in one-page checkout and is required for multi-shipping checkout +input ShippingMethodForAddressInput { + cart_address_id: String! + shipping_method_code: String! +} + +input SetBillingAddressOnCartInput { + customer_address_id: Int + address: CartAddressInput +} + +input SetShippingAddressesOnCartInput { + customer_address_id: Int # Can be provided in one-page checkout and is required for multi-shipping checkout address: CartAddressInput - shipping_method_code: String # Should be on address level for multi-shipping checkout + cart_items: [CartItemQuantityInput!] +} + +input CartItemQuantityInput { + cart_item_id: String! + quantity: Float! } input CartAddressInput { @@ -24,15 +39,18 @@ input CartAddressInput { postcode: String country_code: String! telephone: String! - fax: String save_in_address_book: Boolean! } -type AddShippingAddressToCartOutput { +type SetShippingAddressesOnCartOutput { cart: Cart! } -type AddBillingAddressToCartOutput { +type SetShippingMethodsOnCartOutput { + cart: Cart! +} + +type SetBillingAddressOnCartOutput { cart: Cart! } @@ -50,7 +68,6 @@ type CartAddress { postcode: String country: CartAddressCountry! telephone: String! - fax: String address_type: AdressTypeEnum! selected_shipping_method: CheckoutShippingMethod @@ -60,6 +77,13 @@ type CartAddress { customer_notes: String gift_cards_amount_used: Money applied_gift_cards: [CartGiftCard] + + cart_items: [CartItemQuantity] +} + +type CartItemQuantity { + cart_item_id: String! + quantity: Float! } type CartAddressCountry { @@ -78,7 +102,9 @@ enum AdressTypeEnum { } type CheckoutShippingMethod { - code: String! - label: String! + code: String + label: String free_shipping: Boolean! + error_message: String + # TODO: Add more complex structure for shipping rates }