diff --git a/framework/bigcommerce/api/index.ts b/framework/bigcommerce/api/index.ts index c81107f5e..c9567dc9a 100644 --- a/framework/bigcommerce/api/index.ts +++ b/framework/bigcommerce/api/index.ts @@ -23,6 +23,7 @@ import getSiteInfo from './operations/get-site-info' import getCustomerWishlist from './operations/get-customer-wishlist' import getAllProductPaths from './operations/get-all-product-paths' import getAllProducts from './operations/get-all-products' +import getProduct from './operations/get-product' export interface BigcommerceConfig extends CommerceAPIConfig { // Indicates if the returned metadata with translations should be applied to the @@ -126,6 +127,7 @@ export const provider = { getCustomerWishlist, getAllProductPaths, getAllProducts, + getProduct, }, } diff --git a/framework/bigcommerce/api/operations/get-all-products.ts b/framework/bigcommerce/api/operations/get-all-products.ts index ef9696c73..c5e16a276 100644 --- a/framework/bigcommerce/api/operations/get-all-products.ts +++ b/framework/bigcommerce/api/operations/get-all-products.ts @@ -56,27 +56,9 @@ export type GetAllProductsResult< } > = T -const FIELDS = [ - 'products', - 'featuredProducts', - 'bestSellingProducts', - 'newestProducts', -] - -export type ProductTypes = - | 'products' - | 'featuredProducts' - | 'bestSellingProducts' - | 'newestProducts' - -export type ProductVariables = { field?: ProductTypes } & Omit< - GetAllProductsQueryVariables, - ProductTypes | 'hasLocale' -> - function getProductsType( relevance?: GetAllProductsOperation['variables']['relevance'] -): ProductTypes { +) { switch (relevance) { case 'featured': return 'featuredProducts' @@ -118,19 +100,13 @@ export default function getAllProductsOperation({ } = {}): Promise { config = commerce.getConfig(config) - const locale = config.locale + const { locale } = config const field = getProductsType(vars.relevance) const variables: GetAllProductsQueryVariables = { locale, hasLocale: !!locale, } - if (!FIELDS.includes(field)) { - throw new Error( - `The field variable has to match one of ${FIELDS.join(', ')}` - ) - } - variables[field] = true if (vars.first) variables.first = vars.first diff --git a/framework/bigcommerce/api/operations/get-product.ts b/framework/bigcommerce/api/operations/get-product.ts new file mode 100644 index 000000000..31de42ef6 --- /dev/null +++ b/framework/bigcommerce/api/operations/get-product.ts @@ -0,0 +1,120 @@ +import type { + OperationContext, + OperationOptions, +} from '@commerce/api/operations' +import type { GetProductOperation } from '../../types/product' +import type { GetProductQuery, GetProductQueryVariables } from '../../schema' +import setProductLocaleMeta from '../utils/set-product-locale-meta' +import { productInfoFragment } from '../fragments/product' +import { BigcommerceConfig, Provider } from '..' +import { normalizeProduct } from '../../lib/normalize' + +export const getProductQuery = /* GraphQL */ ` + query getProduct( + $hasLocale: Boolean = false + $locale: String = "null" + $path: String! + ) { + site { + route(path: $path) { + node { + __typename + ... on Product { + ...productInfo + variants { + edges { + node { + entityId + defaultImage { + urlOriginal + altText + isDefault + } + prices { + ...productPrices + } + inventory { + aggregated { + availableToSell + warningLevel + } + isInStock + } + productOptions { + edges { + node { + __typename + entityId + displayName + ...multipleChoiceOption + } + } + } + } + } + } + } + } + } + } + } + + ${productInfoFragment} +` + +// TODO: See if this type is useful for defining the Product type +// export type ProductNode = Extract< +// GetProductQuery['site']['route']['node'], +// { __typename: 'Product' } +// > + +export default function getAllProductPathsOperation({ + commerce, +}: OperationContext) { + async function getProduct(opts: { + variables: T['variables'] + config?: BigcommerceConfig + preview?: boolean + }): Promise + + async function getProduct( + opts: { + variables: T['variables'] + config?: BigcommerceConfig + preview?: boolean + } & OperationOptions + ): Promise + + async function getProduct({ + query = getProductQuery, + variables: { slug, ...vars }, + config, + }: { + query?: string + variables: T['variables'] + config?: BigcommerceConfig + preview?: boolean + }): Promise { + config = commerce.getConfig(config) + + const { locale } = config + const variables: GetProductQueryVariables = { + locale, + hasLocale: !!locale, + path: slug ? `/${slug}/` : vars.path!, + } + const { data } = await config.fetch(query, { variables }) + const product = data.site?.route?.node + + if (product?.__typename === 'Product') { + if (locale && config.applyLocale) { + setProductLocaleMeta(product) + } + + return { product: normalizeProduct(product as any) } + } + + return {} + } + return getProduct +} diff --git a/framework/bigcommerce/product/get-product.ts b/framework/bigcommerce/product/get-product.ts deleted file mode 100644 index b52568b62..000000000 --- a/framework/bigcommerce/product/get-product.ts +++ /dev/null @@ -1,121 +0,0 @@ -import type { GetProductQuery, GetProductQueryVariables } from '../schema' -import setProductLocaleMeta from '../api/utils/set-product-locale-meta' -import { productInfoFragment } from '../api/fragments/product' -import { BigcommerceConfig, getConfig } from '../api' -import { normalizeProduct } from '../lib/normalize' -import type { Product } from '@commerce/types' - -export const getProductQuery = /* GraphQL */ ` - query getProduct( - $hasLocale: Boolean = false - $locale: String = "null" - $path: String! - ) { - site { - route(path: $path) { - node { - __typename - ... on Product { - ...productInfo - variants { - edges { - node { - entityId - defaultImage { - urlOriginal - altText - isDefault - } - prices { - ...productPrices - } - inventory { - aggregated { - availableToSell - warningLevel - } - isInStock - } - productOptions { - edges { - node { - __typename - entityId - displayName - ...multipleChoiceOption - } - } - } - } - } - } - } - } - } - } - } - - ${productInfoFragment} -` - -export type ProductNode = Extract< - GetProductQuery['site']['route']['node'], - { __typename: 'Product' } -> - -export type GetProductResult< - T extends { product?: any } = { product?: ProductNode } -> = T - -export type ProductVariables = { locale?: string } & ( - | { path: string; slug?: never } - | { path?: never; slug: string } -) - -async function getProduct(opts: { - variables: ProductVariables - config?: BigcommerceConfig - preview?: boolean -}): Promise - -async function getProduct(opts: { - query: string - variables: V - config?: BigcommerceConfig - preview?: boolean -}): Promise> - -async function getProduct({ - query = getProductQuery, - variables: { slug, ...vars }, - config, -}: { - query?: string - variables: ProductVariables - config?: BigcommerceConfig - preview?: boolean -}): Promise { - config = getConfig(config) - - const locale = vars.locale || config.locale - const variables: GetProductQueryVariables = { - ...vars, - locale, - hasLocale: !!locale, - path: slug ? `/${slug}/` : vars.path!, - } - const { data } = await config.fetch(query, { variables }) - const product = data.site?.route?.node - - if (product?.__typename === 'Product') { - if (locale && config.applyLocale) { - setProductLocaleMeta(product) - } - - return { product: normalizeProduct(product as any) } - } - - return {} -} - -export default getProduct diff --git a/framework/bigcommerce/product/index.ts b/framework/bigcommerce/product/index.ts index d5b1cefac..426a3edcd 100644 --- a/framework/bigcommerce/product/index.ts +++ b/framework/bigcommerce/product/index.ts @@ -1,4 +1,2 @@ export { default as usePrice } from './use-price' export { default as useSearch } from './use-search' -export { default as getProduct } from './get-product' -export { default as getAllProducts } from '../api/operations/get-all-products' diff --git a/framework/commerce/api/operations.ts b/framework/commerce/api/operations.ts index c11ebf965..aa30f426e 100644 --- a/framework/commerce/api/operations.ts +++ b/framework/commerce/api/operations.ts @@ -6,6 +6,7 @@ import type { GetCustomerWishlistOperation } from '../types/wishlist' import type { GetAllProductPathsOperation, GetAllProductsOperation, + GetProductOperation, } from '../types/product' import type { APIProvider, CommerceAPI } from '.' @@ -128,6 +129,22 @@ export type Operations

= { } & OperationOptions ): Promise } + + getProduct: { + (opts: { + variables: T['variables'] + config?: P['config'] + preview?: boolean + }): Promise + + ( + opts: { + variables: T['variables'] + config?: P['config'] + preview?: boolean + } & OperationOptions + ): Promise + } } export type APIOperations

= { diff --git a/framework/commerce/types/product.ts b/framework/commerce/types/product.ts index ed7e0e85b..f69827f89 100644 --- a/framework/commerce/types/product.ts +++ b/framework/commerce/types/product.ts @@ -81,3 +81,8 @@ export type GetAllProductsOperation = { first?: number } } + +export type GetProductOperation = { + data: { product?: T['product'] } + variables: { path: string; slug?: never } | { path?: never; slug: string } +} diff --git a/pages/product/[slug].tsx b/pages/product/[slug].tsx index 282df7247..6f47e5dfa 100644 --- a/pages/product/[slug].tsx +++ b/pages/product/[slug].tsx @@ -7,9 +7,7 @@ import { useRouter } from 'next/router' import commerce from '@lib/api/commerce' import { Layout } from '@components/common' import { ProductView } from '@components/product' - import { getConfig } from '@framework/api' -import getProduct from '@framework/product/get-product' export async function getStaticProps({ params, @@ -18,7 +16,7 @@ export async function getStaticProps({ }: GetStaticPropsContext<{ slug: string }>) { const config = getConfig({ locale }) const { pages } = await commerce.getAllPages({ config, preview }) - const { product } = await getProduct({ + const { product } = await commerce.getProduct({ variables: { slug: params!.slug }, config, preview,