Updated getProduct operation

This commit is contained in:
Luis Alvarez 2021-05-24 18:39:07 -05:00
parent be5d47bac3
commit a106629964
8 changed files with 147 additions and 152 deletions

View File

@ -23,6 +23,7 @@ import getSiteInfo from './operations/get-site-info'
import getCustomerWishlist from './operations/get-customer-wishlist' import getCustomerWishlist from './operations/get-customer-wishlist'
import getAllProductPaths from './operations/get-all-product-paths' import getAllProductPaths from './operations/get-all-product-paths'
import getAllProducts from './operations/get-all-products' import getAllProducts from './operations/get-all-products'
import getProduct from './operations/get-product'
export interface BigcommerceConfig extends CommerceAPIConfig { export interface BigcommerceConfig extends CommerceAPIConfig {
// Indicates if the returned metadata with translations should be applied to the // Indicates if the returned metadata with translations should be applied to the
@ -126,6 +127,7 @@ export const provider = {
getCustomerWishlist, getCustomerWishlist,
getAllProductPaths, getAllProductPaths,
getAllProducts, getAllProducts,
getProduct,
}, },
} }

View File

@ -56,27 +56,9 @@ export type GetAllProductsResult<
} }
> = T > = 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( function getProductsType(
relevance?: GetAllProductsOperation['variables']['relevance'] relevance?: GetAllProductsOperation['variables']['relevance']
): ProductTypes { ) {
switch (relevance) { switch (relevance) {
case 'featured': case 'featured':
return 'featuredProducts' return 'featuredProducts'
@ -118,19 +100,13 @@ export default function getAllProductsOperation({
} = {}): Promise<T['data']> { } = {}): Promise<T['data']> {
config = commerce.getConfig(config) config = commerce.getConfig(config)
const locale = config.locale const { locale } = config
const field = getProductsType(vars.relevance) const field = getProductsType(vars.relevance)
const variables: GetAllProductsQueryVariables = { const variables: GetAllProductsQueryVariables = {
locale, locale,
hasLocale: !!locale, hasLocale: !!locale,
} }
if (!FIELDS.includes(field)) {
throw new Error(
`The field variable has to match one of ${FIELDS.join(', ')}`
)
}
variables[field] = true variables[field] = true
if (vars.first) variables.first = vars.first if (vars.first) variables.first = vars.first

View File

@ -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<Provider>) {
async function getProduct<T extends GetProductOperation>(opts: {
variables: T['variables']
config?: BigcommerceConfig
preview?: boolean
}): Promise<T['data']>
async function getProduct<T extends GetProductOperation>(
opts: {
variables: T['variables']
config?: BigcommerceConfig
preview?: boolean
} & OperationOptions
): Promise<T['data']>
async function getProduct<T extends GetProductOperation>({
query = getProductQuery,
variables: { slug, ...vars },
config,
}: {
query?: string
variables: T['variables']
config?: BigcommerceConfig
preview?: boolean
}): Promise<T['data']> {
config = commerce.getConfig(config)
const { locale } = config
const variables: GetProductQueryVariables = {
locale,
hasLocale: !!locale,
path: slug ? `/${slug}/` : vars.path!,
}
const { data } = await config.fetch<GetProductQuery>(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
}

View File

@ -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<GetProductResult>
async function getProduct<T extends { product?: any }, V = any>(opts: {
query: string
variables: V
config?: BigcommerceConfig
preview?: boolean
}): Promise<GetProductResult<T>>
async function getProduct({
query = getProductQuery,
variables: { slug, ...vars },
config,
}: {
query?: string
variables: ProductVariables
config?: BigcommerceConfig
preview?: boolean
}): Promise<Product | {} | any> {
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<GetProductQuery>(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

View File

@ -1,4 +1,2 @@
export { default as usePrice } from './use-price' export { default as usePrice } from './use-price'
export { default as useSearch } from './use-search' export { default as useSearch } from './use-search'
export { default as getProduct } from './get-product'
export { default as getAllProducts } from '../api/operations/get-all-products'

View File

@ -6,6 +6,7 @@ import type { GetCustomerWishlistOperation } from '../types/wishlist'
import type { import type {
GetAllProductPathsOperation, GetAllProductPathsOperation,
GetAllProductsOperation, GetAllProductsOperation,
GetProductOperation,
} from '../types/product' } from '../types/product'
import type { APIProvider, CommerceAPI } from '.' import type { APIProvider, CommerceAPI } from '.'
@ -128,6 +129,22 @@ export type Operations<P extends APIProvider> = {
} & OperationOptions } & OperationOptions
): Promise<T['data']> ): Promise<T['data']>
} }
getProduct: {
<T extends GetProductOperation>(opts: {
variables: T['variables']
config?: P['config']
preview?: boolean
}): Promise<T['data']>
<T extends GetProductOperation>(
opts: {
variables: T['variables']
config?: P['config']
preview?: boolean
} & OperationOptions
): Promise<T['data']>
}
} }
export type APIOperations<P extends APIProvider> = { export type APIOperations<P extends APIProvider> = {

View File

@ -81,3 +81,8 @@ export type GetAllProductsOperation<T extends ProductTypes = ProductTypes> = {
first?: number first?: number
} }
} }
export type GetProductOperation<T extends ProductTypes = ProductTypes> = {
data: { product?: T['product'] }
variables: { path: string; slug?: never } | { path?: never; slug: string }
}

View File

@ -7,9 +7,7 @@ import { useRouter } from 'next/router'
import commerce from '@lib/api/commerce' import commerce from '@lib/api/commerce'
import { Layout } from '@components/common' import { Layout } from '@components/common'
import { ProductView } from '@components/product' import { ProductView } from '@components/product'
import { getConfig } from '@framework/api' import { getConfig } from '@framework/api'
import getProduct from '@framework/product/get-product'
export async function getStaticProps({ export async function getStaticProps({
params, params,
@ -18,7 +16,7 @@ export async function getStaticProps({
}: GetStaticPropsContext<{ slug: string }>) { }: GetStaticPropsContext<{ slug: string }>) {
const config = getConfig({ locale }) const config = getConfig({ locale })
const { pages } = await commerce.getAllPages({ config, preview }) const { pages } = await commerce.getAllPages({ config, preview })
const { product } = await getProduct({ const { product } = await commerce.getProduct({
variables: { slug: params!.slug }, variables: { slug: params!.slug },
config, config,
preview, preview,