From cdf8005c21cf848757c51bcc5941f0e163bd379a Mon Sep 17 00:00:00 2001 From: cond0r Date: Thu, 29 Apr 2021 19:22:22 +0300 Subject: [PATCH] Update product mutations & queries --- .../shopify/api/utils/fetch-all-products.ts | 17 ++++--- .../shopify/product/get-all-product-paths.ts | 4 +- framework/shopify/product/get-all-products.ts | 22 +++----- framework/shopify/product/get-product.ts | 20 ++++---- framework/shopify/product/use-search.tsx | 51 ++++++++++++------- framework/shopify/schema.d.ts | 46 ++++++++++++----- framework/shopify/schema.graphql | 42 ++++++++++++++- framework/shopify/types.ts | 2 + framework/shopify/utils/checkout-to-cart.ts | 6 --- .../utils/mutations/checkout-create.ts | 4 +- .../utils/mutations/checkout-line-item-add.ts | 5 +- .../mutations/checkout-line-item-remove.ts | 2 +- .../mutations/checkout-line-item-update.ts | 5 +- framework/shopify/utils/normalize.ts | 38 +++++++------- .../utils/queries/get-checkout-query.ts | 2 +- .../shopify/utils/queries/get-page-query.ts | 2 +- 16 files changed, 170 insertions(+), 98 deletions(-) diff --git a/framework/shopify/api/utils/fetch-all-products.ts b/framework/shopify/api/utils/fetch-all-products.ts index 9fa70a5ee..b4eadc67a 100644 --- a/framework/shopify/api/utils/fetch-all-products.ts +++ b/framework/shopify/api/utils/fetch-all-products.ts @@ -1,6 +1,12 @@ -import { ProductEdge } from '../../schema' +import { + GetAllProductPathsQuery, + GetAllProductVendorsQuery, + ProductEdge, +} from '../../schema' import { ShopifyConfig } from '..' +type FetchAllProductsQuery = GetAllProductPathsQuery | GetAllProductVendorsQuery + const fetchAllProducts = async ({ config, query, @@ -10,19 +16,18 @@ const fetchAllProducts = async ({ }: { config: ShopifyConfig query: string - acc?: ProductEdge[] + acc?: any[] variables?: any cursor?: string }): Promise => { - const { data } = await config.fetch(query, { + const { data } = await config.fetch(query, { variables: { ...variables, cursor }, }) - const edges: ProductEdge[] = data.products?.edges ?? [] - const hasNextPage = data.products?.pageInfo?.hasNextPage + const edges = data.products.edges acc = acc.concat(edges) - if (hasNextPage) { + if (data.products.pageInfo.hasNextPage) { const cursor = edges.pop()?.cursor if (cursor) { return fetchAllProducts({ diff --git a/framework/shopify/product/get-all-product-paths.ts b/framework/shopify/product/get-all-product-paths.ts index 4431d1e53..f95b533d4 100644 --- a/framework/shopify/product/get-all-product-paths.ts +++ b/framework/shopify/product/get-all-product-paths.ts @@ -1,7 +1,5 @@ -import { Product } from '@commerce/types' import { getConfig, ShopifyConfig } from '../api' import fetchAllProducts from '../api/utils/fetch-all-products' -import { ProductEdge } from '../schema' import getAllProductsPathsQuery from '../utils/queries/get-all-products-paths-query' type ProductPath = { @@ -31,7 +29,7 @@ const getAllProductPaths = async (options?: { }) return { - products: products?.map(({ node: { handle } }: ProductEdge) => ({ + products: products?.map(({ node: { handle } }) => ({ node: { path: `/${handle}`, }, diff --git a/framework/shopify/product/get-all-products.ts b/framework/shopify/product/get-all-products.ts index 14e486563..3d74448dc 100644 --- a/framework/shopify/product/get-all-products.ts +++ b/framework/shopify/product/get-all-products.ts @@ -1,6 +1,5 @@ -import { GraphQLFetcherResult } from '@commerce/api' import { getConfig, ShopifyConfig } from '../api' -import { ProductEdge } from '../schema' +import { GetAllProductsQuery, Product as ShopifyProduct } from '../schema' import { getAllProductsQuery } from '../utils/queries' import { normalizeProduct } from '../utils/normalize' import { Product } from '@commerce/types' @@ -10,30 +9,25 @@ type Variables = { field?: string } -type ReturnType = { - products: Product[] -} - const getAllProducts = async (options: { variables?: Variables config?: ShopifyConfig preview?: boolean -}): Promise => { +}): Promise<{ + products: Product[] +}> => { let { config, variables = { first: 250 } } = options ?? {} config = getConfig(config) - const { data }: GraphQLFetcherResult = await config.fetch( + const { data } = await config.fetch( getAllProductsQuery, { variables } ) - const products = - data.products?.edges?.map(({ node: p }: ProductEdge) => - normalizeProduct(p) - ) ?? [] - return { - products, + products: data.products.edges.map(({ node }) => + normalizeProduct(node as ShopifyProduct) + ), } } diff --git a/framework/shopify/product/get-product.ts b/framework/shopify/product/get-product.ts index 1d861e1a1..a3b634fcd 100644 --- a/framework/shopify/product/get-product.ts +++ b/framework/shopify/product/get-product.ts @@ -1,30 +1,32 @@ -import { GraphQLFetcherResult } from '@commerce/api' +import { GetProductBySlugQuery, Product as ShopifyProduct } from '../schema' import { getConfig, ShopifyConfig } from '../api' import { normalizeProduct, getProductQuery } from '../utils' +import { Product } from '@commerce/types' type Variables = { slug: string } -type ReturnType = { - product: any -} - const getProduct = async (options: { variables: Variables config: ShopifyConfig preview?: boolean -}): Promise => { +}): Promise<{ + product?: Product +}> => { let { config, variables } = options ?? {} config = getConfig(config) - const { data }: GraphQLFetcherResult = await config.fetch(getProductQuery, { + const { + data: { productByHandle }, + } = await config.fetch(getProductQuery, { variables, }) - const { productByHandle } = data return { - product: productByHandle ? normalizeProduct(productByHandle) : null, + ...(productByHandle && { + product: normalizeProduct(productByHandle as ShopifyProduct), + }), } } diff --git a/framework/shopify/product/use-search.tsx b/framework/shopify/product/use-search.tsx index bf812af3d..16ac8b249 100644 --- a/framework/shopify/product/use-search.tsx +++ b/framework/shopify/product/use-search.tsx @@ -1,7 +1,12 @@ import { SWRHook } from '@commerce/utils/types' import useSearch, { UseSearch } from '@commerce/product/use-search' -import { ProductEdge } from '../schema' +import { + CollectionEdge, + GetAllProductsQuery, + Product as ShopifyProduct, + ProductEdge, +} from '../schema' import { getAllProductsQuery, getCollectionProductsQuery, @@ -35,30 +40,38 @@ export const handler: SWRHook< }, async fetcher({ input, options, fetch }) { const { categoryId, brandId } = input + const method = options?.method + const variables = getSearchVariables(input) + let products - const data = await fetch({ - query: categoryId ? getCollectionProductsQuery : options.query, - method: options?.method, - variables: getSearchVariables(input), - }) - - let edges - + // change the query to getCollectionProductsQuery when categoryId is set if (categoryId) { - edges = data.node?.products?.edges ?? [] - if (brandId) { - edges = edges.filter( - ({ node: { vendor } }: ProductEdge) => - vendor.replace(/\s+/g, '-').toLowerCase() === brandId - ) - } + const data = await fetch({ + query: getCollectionProductsQuery, + method, + variables, + }) + // filter on client when brandId & categoryId are set since is not available on collection product query + products = brandId + ? data.node.products.edges.filter( + ({ node: { vendor } }: ProductEdge) => + vendor.replace(/\s+/g, '-').toLowerCase() === brandId + ) + : data.node.products.edges } else { - edges = data.products?.edges ?? [] + const data = await fetch({ + query: options.query, + method, + variables, + }) + products = data.products.edges } return { - products: edges.map(({ node }: ProductEdge) => normalizeProduct(node)), - found: !!edges.length, + products: products?.map(({ node }) => + normalizeProduct(node as ShopifyProduct) + ), + found: !!products?.length, } }, useHook: ({ useData }) => (input = {}) => { diff --git a/framework/shopify/schema.d.ts b/framework/shopify/schema.d.ts index 7477df239..87bf762fe 100644 --- a/framework/shopify/schema.d.ts +++ b/framework/shopify/schema.d.ts @@ -37,7 +37,7 @@ export type ApiVersion = { displayName: Scalars['String'] /** The unique identifier of an ApiVersion. All supported API versions have a date-based (YYYY-MM) or `unstable` handle. */ handle: Scalars['String'] - /** Whether the version is supported by Shopify. */ + /** Whether the version is actively supported by Shopify. Supported API versions are guaranteed to be stable. Unsupported API versions include unstable, release candidate, and end-of-life versions that are marked as unsupported. For more information, refer to [Versioning](https://shopify.dev/concepts/about-apis/versioning). */ supported: Scalars['Boolean'] } @@ -393,6 +393,8 @@ export type Checkout = Node & { taxExempt: Scalars['Boolean'] /** Specifies if taxes are included in the line item and shipping line prices. */ taxesIncluded: Scalars['Boolean'] + /** The sum of all the duties applied to the line items in the checkout. */ + totalDuties?: Maybe /** * The sum of all the prices of all the items in the checkout, taxes and discounts included. * @deprecated Use `totalPriceV2` instead @@ -1661,6 +1663,8 @@ export enum CountryCode { Zm = 'ZM', /** Zimbabwe. */ Zw = 'ZW', + /** Unknown Region. */ + Zz = 'ZZ', } /** Credit card information used for a payment. */ @@ -2561,6 +2565,8 @@ export type ExternalVideo = Node & alt?: Maybe /** The URL. */ embeddedUrl: Scalars['URL'] + /** The host of the external video. */ + host: MediaHost /** Globally unique identifier. */ id: Scalars['ID'] /** The media content type. */ @@ -2935,6 +2941,14 @@ export type MediaEdge = { node: Media } +/** Host for a Media Resource. */ +export enum MediaHost { + /** Host for YouTube embedded videos. */ + Youtube = 'YOUTUBE', + /** Host for Vimeo embedded videos. */ + Vimeo = 'VIMEO', +} + /** Represents a Shopify hosted image. */ export type MediaImage = Node & Media & { @@ -3503,6 +3517,8 @@ export type Order = Node & { currencyCode: CurrencyCode /** The subtotal of line items and their discounts, excluding line items that have been removed. Does not contain order-level discounts, duties, shipping costs, or shipping discounts. Taxes are not included unless the order is a taxes-included order. */ currentSubtotalPrice: MoneyV2 + /** The total cost of duties for the order, including refunds. */ + currentTotalDuties?: Maybe /** The total amount of the order, including duties, taxes and discounts, minus amounts for line items that have been removed. */ currentTotalPrice: MoneyV2 /** The total of all taxes applied to the order, excluding taxes for returned line items. */ @@ -3532,6 +3548,8 @@ export type Order = Node & { name: Scalars['String'] /** A unique numeric identifier for the order for use by shop owner and customer. */ orderNumber: Scalars['Int'] + /** The total cost of duties charged at checkout. */ + originalTotalDuties?: Maybe /** The total price of the order before any applied edits. */ originalTotalPrice: MoneyV2 /** The customer's phone number for receiving SMS notifications. */ @@ -5013,9 +5031,11 @@ export type AssociateCustomerWithCheckoutMutation = { > } -export type Unnamed_1_MutationVariables = Exact<{ [key: string]: never }> +export type CheckoutCreateMutationVariables = Exact<{ + input?: Maybe +}> -export type Unnamed_1_Mutation = { __typename?: 'Mutation' } & { +export type CheckoutCreateMutation = { __typename?: 'Mutation' } & { checkoutCreate?: Maybe< { __typename?: 'CheckoutCreatePayload' } & { checkoutUserErrors: Array< @@ -5029,12 +5049,12 @@ export type Unnamed_1_Mutation = { __typename?: 'Mutation' } & { > } -export type Unnamed_2_MutationVariables = Exact<{ +export type CheckoutLineItemAddMutationVariables = Exact<{ checkoutId: Scalars['ID'] lineItems: Array | CheckoutLineItemInput }> -export type Unnamed_2_Mutation = { __typename?: 'Mutation' } & { +export type CheckoutLineItemAddMutation = { __typename?: 'Mutation' } & { checkoutLineItemsAdd?: Maybe< { __typename?: 'CheckoutLineItemsAddPayload' } & { checkoutUserErrors: Array< @@ -5048,12 +5068,12 @@ export type Unnamed_2_Mutation = { __typename?: 'Mutation' } & { > } -export type Unnamed_3_MutationVariables = Exact<{ +export type CheckoutLineItemRemoveMutationVariables = Exact<{ checkoutId: Scalars['ID'] lineItemIds: Array | Scalars['ID'] }> -export type Unnamed_3_Mutation = { __typename?: 'Mutation' } & { +export type CheckoutLineItemRemoveMutation = { __typename?: 'Mutation' } & { checkoutLineItemsRemove?: Maybe< { __typename?: 'CheckoutLineItemsRemovePayload' } & { checkoutUserErrors: Array< @@ -5067,12 +5087,12 @@ export type Unnamed_3_Mutation = { __typename?: 'Mutation' } & { > } -export type Unnamed_4_MutationVariables = Exact<{ +export type CheckoutLineItemUpdateMutationVariables = Exact<{ checkoutId: Scalars['ID'] lineItems: Array | CheckoutLineItemUpdateInput }> -export type Unnamed_4_Mutation = { __typename?: 'Mutation' } & { +export type CheckoutLineItemUpdateMutation = { __typename?: 'Mutation' } & { checkoutLineItemsUpdate?: Maybe< { __typename?: 'CheckoutLineItemsUpdatePayload' } & { checkoutUserErrors: Array< @@ -5370,11 +5390,11 @@ export type CheckoutDetailsFragment = { __typename?: 'Checkout' } & Pick< } } -export type Unnamed_5_QueryVariables = Exact<{ +export type GetCheckoutQueryVariables = Exact<{ checkoutId: Scalars['ID'] }> -export type Unnamed_5_Query = { __typename?: 'QueryRoot' } & { +export type GetCheckoutQuery = { __typename?: 'QueryRoot' } & { node?: Maybe< | { __typename?: 'AppliedGiftCard' } | { __typename?: 'Article' } @@ -5464,11 +5484,11 @@ export type GetCustomerQuery = { __typename?: 'QueryRoot' } & { > } -export type Unnamed_6_QueryVariables = Exact<{ +export type GetPageQueryVariables = Exact<{ id: Scalars['ID'] }> -export type Unnamed_6_Query = { __typename?: 'QueryRoot' } & { +export type GetPageQuery = { __typename?: 'QueryRoot' } & { node?: Maybe< | ({ __typename?: 'AppliedGiftCard' } & Pick) | ({ __typename?: 'Article' } & Pick) diff --git a/framework/shopify/schema.graphql b/framework/shopify/schema.graphql index fa7c9c93e..95b7c11ab 100644 --- a/framework/shopify/schema.graphql +++ b/framework/shopify/schema.graphql @@ -28,7 +28,7 @@ type ApiVersion { handle: String! """ - Whether the version is supported by Shopify. + Whether the version is actively supported by Shopify. Supported API versions are guaranteed to be stable. Unsupported API versions include unstable, release candidate, and end-of-life versions that are marked as unsupported. For more information, refer to [Versioning](https://shopify.dev/concepts/about-apis/versioning). """ supported: Boolean! } @@ -769,6 +769,11 @@ type Checkout implements Node { """ taxesIncluded: Boolean! + """ + The sum of all the duties applied to the line items in the checkout. + """ + totalDuties: MoneyV2 + """ The sum of all the prices of all the items in the checkout, taxes and discounts included. """ @@ -3306,6 +3311,11 @@ enum CountryCode { Zimbabwe. """ ZW + + """ + Unknown Region. + """ + ZZ } """ @@ -5178,6 +5188,11 @@ type ExternalVideo implements Node & Media { """ embeddedUrl: URL! + """ + The host of the external video. + """ + host: MediaHost! + """ Globally unique identifier. """ @@ -5803,6 +5818,21 @@ type MediaEdge { node: Media! } +""" +Host for a Media Resource. +""" +enum MediaHost { + """ + Host for YouTube embedded videos. + """ + YOUTUBE + + """ + Host for Vimeo embedded videos. + """ + VIMEO +} + """ Represents a Shopify hosted image. """ @@ -6745,6 +6775,11 @@ type Order implements Node { """ currentSubtotalPrice: MoneyV2! + """ + The total cost of duties for the order, including refunds. + """ + currentTotalDuties: MoneyV2 + """ The total amount of the order, including duties, taxes and discounts, minus amounts for line items that have been removed. """ @@ -6861,6 +6896,11 @@ type Order implements Node { """ orderNumber: Int! + """ + The total cost of duties charged at checkout. + """ + originalTotalDuties: MoneyV2 + """ The total price of the order before any applied edits. """ diff --git a/framework/shopify/types.ts b/framework/shopify/types.ts index e7bcb2476..ba0f13997 100644 --- a/framework/shopify/types.ts +++ b/framework/shopify/types.ts @@ -9,7 +9,9 @@ export type ShopifyCheckout = { export type Cart = Core.Cart & { lineItems: LineItem[] + url?: String } + export interface LineItem extends Core.LineItem { options?: any[] } diff --git a/framework/shopify/utils/checkout-to-cart.ts b/framework/shopify/utils/checkout-to-cart.ts index 034ff11d7..63e4a18ab 100644 --- a/framework/shopify/utils/checkout-to-cart.ts +++ b/framework/shopify/utils/checkout-to-cart.ts @@ -27,12 +27,6 @@ export type CheckoutPayload = | CheckoutQuery const checkoutToCart = (checkoutPayload?: Maybe): Cart => { - if (!checkoutPayload) { - throw new CommerceError({ - message: 'Missing checkout payload from response', - }) - } - const checkout = checkoutPayload?.checkout throwUserErrors(checkoutPayload?.checkoutUserErrors) diff --git a/framework/shopify/utils/mutations/checkout-create.ts b/framework/shopify/utils/mutations/checkout-create.ts index f44619f30..7bff7e757 100644 --- a/framework/shopify/utils/mutations/checkout-create.ts +++ b/framework/shopify/utils/mutations/checkout-create.ts @@ -1,8 +1,8 @@ import { checkoutDetailsFragment } from '../queries/get-checkout-query' const checkoutCreateMutation = /* GraphQL */ ` - mutation { - checkoutCreate(input: {}) { + mutation checkoutCreate($input: CheckoutCreateInput = {}) { + checkoutCreate(input: $input) { checkoutUserErrors { code field diff --git a/framework/shopify/utils/mutations/checkout-line-item-add.ts b/framework/shopify/utils/mutations/checkout-line-item-add.ts index 5171e7f4a..02f5b7107 100644 --- a/framework/shopify/utils/mutations/checkout-line-item-add.ts +++ b/framework/shopify/utils/mutations/checkout-line-item-add.ts @@ -1,7 +1,10 @@ import { checkoutDetailsFragment } from '../queries/get-checkout-query' const checkoutLineItemAddMutation = /* GraphQL */ ` - mutation($checkoutId: ID!, $lineItems: [CheckoutLineItemInput!]!) { + mutation checkoutLineItemAdd( + $checkoutId: ID! + $lineItems: [CheckoutLineItemInput!]! + ) { checkoutLineItemsAdd(checkoutId: $checkoutId, lineItems: $lineItems) { checkoutUserErrors { code diff --git a/framework/shopify/utils/mutations/checkout-line-item-remove.ts b/framework/shopify/utils/mutations/checkout-line-item-remove.ts index 26ebfca2e..30cb83028 100644 --- a/framework/shopify/utils/mutations/checkout-line-item-remove.ts +++ b/framework/shopify/utils/mutations/checkout-line-item-remove.ts @@ -1,7 +1,7 @@ import { checkoutDetailsFragment } from '../queries/get-checkout-query' const checkoutLineItemRemoveMutation = /* GraphQL */ ` - mutation($checkoutId: ID!, $lineItemIds: [ID!]!) { + mutation checkoutLineItemRemove($checkoutId: ID!, $lineItemIds: [ID!]!) { checkoutLineItemsRemove( checkoutId: $checkoutId lineItemIds: $lineItemIds diff --git a/framework/shopify/utils/mutations/checkout-line-item-update.ts b/framework/shopify/utils/mutations/checkout-line-item-update.ts index 644882360..fca617fb7 100644 --- a/framework/shopify/utils/mutations/checkout-line-item-update.ts +++ b/framework/shopify/utils/mutations/checkout-line-item-update.ts @@ -1,7 +1,10 @@ import { checkoutDetailsFragment } from '../queries/get-checkout-query' const checkoutLineItemUpdateMutation = /* GraphQL */ ` - mutation($checkoutId: ID!, $lineItems: [CheckoutLineItemUpdateInput!]!) { + mutation checkoutLineItemUpdate( + $checkoutId: ID! + $lineItems: [CheckoutLineItemUpdateInput!]! + ) { checkoutLineItemsUpdate(checkoutId: $checkoutId, lineItems: $lineItems) { checkoutUserErrors { code diff --git a/framework/shopify/utils/normalize.ts b/framework/shopify/utils/normalize.ts index 4ebc3a1ae..472723afe 100644 --- a/framework/shopify/utils/normalize.ts +++ b/framework/shopify/utils/normalize.ts @@ -75,22 +75,21 @@ const normalizeProductVariants = ({ edges }: ProductVariantConnection) => { ) } -export function normalizeProduct(productNode: ShopifyProduct): Product { - const { - id, - title: name, - vendor, - images, - variants, - description, - descriptionHtml, - handle, - priceRange, - options, - ...rest - } = productNode - - const product = { +export function normalizeProduct({ + id, + title: name, + vendor, + images, + variants, + description, + descriptionHtml, + handle, + priceRange, + options, + metafields, + ...rest +}: ShopifyProduct): Product { + return { id, name, vendor, @@ -108,13 +107,12 @@ export function normalizeProduct(productNode: ShopifyProduct): Product { ...(descriptionHtml && { descriptionHtml }), ...rest, } - - return product } export function normalizeCart(checkout: Checkout): Cart { return { id: checkout.id, + url: checkout.webUrl, customerId: '', email: '', createdAt: checkout.createdAt, @@ -131,7 +129,7 @@ export function normalizeCart(checkout: Checkout): Cart { } function normalizeLineItem({ - node: { id, title, variant, quantity, ...rest }, + node: { id, title, variant, quantity }, }: CheckoutLineItemEdge): LineItem { return { id, @@ -144,7 +142,7 @@ function normalizeLineItem({ sku: variant?.sku ?? '', name: variant?.title!, image: { - url: variant?.image?.originalSrc ?? '/product-img-placeholder.svg', + url: variant?.image?.originalSrc || '/product-img-placeholder.svg', }, requiresShipping: variant?.requiresShipping ?? false, price: variant?.priceV2?.amount, diff --git a/framework/shopify/utils/queries/get-checkout-query.ts b/framework/shopify/utils/queries/get-checkout-query.ts index e755c0a9d..f4f6f33c2 100644 --- a/framework/shopify/utils/queries/get-checkout-query.ts +++ b/framework/shopify/utils/queries/get-checkout-query.ts @@ -56,7 +56,7 @@ export const checkoutDetailsFragment = /* GraphQL */ ` ` const getCheckoutQuery = /* GraphQL */ ` - query($checkoutId: ID!) { + query getCheckout($checkoutId: ID!) { node(id: $checkoutId) { ...checkoutDetails } diff --git a/framework/shopify/utils/queries/get-page-query.ts b/framework/shopify/utils/queries/get-page-query.ts index 2ca79abd4..7939f0278 100644 --- a/framework/shopify/utils/queries/get-page-query.ts +++ b/framework/shopify/utils/queries/get-page-query.ts @@ -1,5 +1,5 @@ export const getPageQuery = /* GraphQL */ ` - query($id: ID!) { + query getPage($id: ID!) { node(id: $id) { id ... on Page {