mirror of
https://github.com/vercel/commerce.git
synced 2025-06-19 05:31:22 +00:00
Moved getAllProducts
This commit is contained in:
parent
c2a5904e01
commit
be5d47bac3
@ -1,6 +1,5 @@
|
||||
import { Product } from '@commerce/types/product'
|
||||
import { ProductsEndpoint } from '.'
|
||||
import getAllProducts from '../../../../product/get-all-products'
|
||||
|
||||
const SORT: { [key: string]: string | undefined } = {
|
||||
latest: 'id',
|
||||
@ -15,6 +14,7 @@ const getProducts: ProductsEndpoint['handlers']['getProducts'] = async ({
|
||||
res,
|
||||
body: { search, category, brand, sort },
|
||||
config,
|
||||
commerce,
|
||||
}) => {
|
||||
// Use a dummy base as we only care about the relative path
|
||||
const url = new URL('/v3/catalog/products', 'http://a')
|
||||
@ -47,18 +47,18 @@ const getProducts: ProductsEndpoint['handlers']['getProducts'] = async ({
|
||||
url.pathname + url.search
|
||||
)
|
||||
|
||||
const entityIds = data.map((p) => p.id)
|
||||
const found = entityIds.length > 0
|
||||
const ids = data.map((p) => String(p.id))
|
||||
const found = ids.length > 0
|
||||
|
||||
// We want the GraphQL version of each product
|
||||
const graphqlData = await getAllProducts({
|
||||
variables: { first: LIMIT, entityIds },
|
||||
const graphqlData = await commerce.getAllProducts({
|
||||
variables: { first: LIMIT, ids },
|
||||
config,
|
||||
})
|
||||
|
||||
// Put the products in an object that we can use to get them by id
|
||||
const productsById = graphqlData.products.reduce<{
|
||||
[k: number]: Product
|
||||
[k: string]: Product
|
||||
}>((prods, p) => {
|
||||
prods[Number(p.id)] = p
|
||||
return prods
|
||||
@ -68,7 +68,7 @@ const getProducts: ProductsEndpoint['handlers']['getProducts'] = async ({
|
||||
|
||||
// Populate the products array with the graphql products, in the order
|
||||
// assigned by the list of entity ids
|
||||
entityIds.forEach((id) => {
|
||||
ids.forEach((id) => {
|
||||
const product = productsById[id]
|
||||
if (product) products.push(product)
|
||||
})
|
||||
|
@ -22,6 +22,7 @@ import getPage from './operations/get-page'
|
||||
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'
|
||||
|
||||
export interface BigcommerceConfig extends CommerceAPIConfig {
|
||||
// Indicates if the returned metadata with translations should be applied to the
|
||||
@ -124,6 +125,7 @@ export const provider = {
|
||||
getSiteInfo,
|
||||
getCustomerWishlist,
|
||||
getAllProductPaths,
|
||||
getAllProducts,
|
||||
},
|
||||
}
|
||||
|
||||
|
160
framework/bigcommerce/api/operations/get-all-products.ts
Normal file
160
framework/bigcommerce/api/operations/get-all-products.ts
Normal file
@ -0,0 +1,160 @@
|
||||
import type {
|
||||
OperationContext,
|
||||
OperationOptions,
|
||||
} from '@commerce/api/operations'
|
||||
import type {
|
||||
GetAllProductsQuery,
|
||||
GetAllProductsQueryVariables,
|
||||
} from '../../schema'
|
||||
import type { GetAllProductsOperation } from '../../types/product'
|
||||
import type { RecursivePartial, RecursiveRequired } from '../utils/types'
|
||||
import filterEdges from '../utils/filter-edges'
|
||||
import setProductLocaleMeta from '../utils/set-product-locale-meta'
|
||||
import { productConnectionFragment } from '../fragments/product'
|
||||
import { BigcommerceConfig, Provider } from '..'
|
||||
import { normalizeProduct } from '../../lib/normalize'
|
||||
|
||||
export const getAllProductsQuery = /* GraphQL */ `
|
||||
query getAllProducts(
|
||||
$hasLocale: Boolean = false
|
||||
$locale: String = "null"
|
||||
$entityIds: [Int!]
|
||||
$first: Int = 10
|
||||
$products: Boolean = false
|
||||
$featuredProducts: Boolean = false
|
||||
$bestSellingProducts: Boolean = false
|
||||
$newestProducts: Boolean = false
|
||||
) {
|
||||
site {
|
||||
products(first: $first, entityIds: $entityIds) @include(if: $products) {
|
||||
...productConnnection
|
||||
}
|
||||
featuredProducts(first: $first) @include(if: $featuredProducts) {
|
||||
...productConnnection
|
||||
}
|
||||
bestSellingProducts(first: $first) @include(if: $bestSellingProducts) {
|
||||
...productConnnection
|
||||
}
|
||||
newestProducts(first: $first) @include(if: $newestProducts) {
|
||||
...productConnnection
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
${productConnectionFragment}
|
||||
`
|
||||
|
||||
export type ProductEdge = NonNullable<
|
||||
NonNullable<GetAllProductsQuery['site']['products']['edges']>[0]
|
||||
>
|
||||
|
||||
export type ProductNode = ProductEdge['node']
|
||||
|
||||
export type GetAllProductsResult<
|
||||
T extends Record<keyof GetAllProductsResult, any[]> = {
|
||||
products: ProductEdge[]
|
||||
}
|
||||
> = 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'
|
||||
case 'best_selling':
|
||||
return 'bestSellingProducts'
|
||||
case 'newest':
|
||||
return 'newestProducts'
|
||||
default:
|
||||
return 'products'
|
||||
}
|
||||
}
|
||||
|
||||
export default function getAllProductsOperation({
|
||||
commerce,
|
||||
}: OperationContext<Provider>) {
|
||||
async function getAllProducts<T extends GetAllProductsOperation>(opts?: {
|
||||
variables?: T['variables']
|
||||
config?: BigcommerceConfig
|
||||
preview?: boolean
|
||||
}): Promise<T['data']>
|
||||
|
||||
async function getAllProducts<T extends GetAllProductsOperation>(
|
||||
opts: {
|
||||
variables?: T['variables']
|
||||
config?: BigcommerceConfig
|
||||
preview?: boolean
|
||||
} & OperationOptions
|
||||
): Promise<T['data']>
|
||||
|
||||
async function getAllProducts<T extends GetAllProductsOperation>({
|
||||
query = getAllProductsQuery,
|
||||
variables: vars = {},
|
||||
config,
|
||||
}: {
|
||||
query?: string
|
||||
variables?: T['variables']
|
||||
config?: BigcommerceConfig
|
||||
preview?: boolean
|
||||
} = {}): Promise<T['data']> {
|
||||
config = commerce.getConfig(config)
|
||||
|
||||
const locale = config.locale
|
||||
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
|
||||
if (vars.ids) variables.entityIds = vars.ids.map((id) => Number(id))
|
||||
|
||||
// RecursivePartial forces the method to check for every prop in the data, which is
|
||||
// required in case there's a custom `query`
|
||||
const { data } = await config.fetch<RecursivePartial<GetAllProductsQuery>>(
|
||||
query,
|
||||
{ variables }
|
||||
)
|
||||
const edges = data.site?.[field]?.edges
|
||||
const products = filterEdges(edges as RecursiveRequired<typeof edges>)
|
||||
|
||||
if (locale && config.applyLocale) {
|
||||
products.forEach((product: RecursivePartial<ProductEdge>) => {
|
||||
if (product.node) setProductLocaleMeta(product.node)
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
products: products.map(({ node }) => normalizeProduct(node as any)),
|
||||
}
|
||||
}
|
||||
|
||||
return getAllProducts
|
||||
}
|
@ -8,7 +8,7 @@ import type {
|
||||
} from '../../types/wishlist'
|
||||
import type { RecursivePartial, RecursiveRequired } from '../utils/types'
|
||||
import { BigcommerceConfig, Provider } from '..'
|
||||
import getAllProducts, { ProductEdge } from '../../product/get-all-products'
|
||||
import getAllProducts, { ProductEdge } from './get-all-products'
|
||||
|
||||
export default function getCustomerWishlistOperation({
|
||||
commerce,
|
||||
@ -47,13 +47,13 @@ export default function getCustomerWishlistOperation({
|
||||
const wishlist = data[0]
|
||||
|
||||
if (includeProducts && wishlist?.items?.length) {
|
||||
const entityIds = wishlist.items
|
||||
?.map((item) => item?.product_id)
|
||||
.filter((id): id is number => !!id)
|
||||
const ids = wishlist.items
|
||||
?.map((item) => (item?.product_id ? String(item?.product_id) : null))
|
||||
.filter((id): id is string => !!id)
|
||||
|
||||
if (entityIds?.length) {
|
||||
const graphqlData = await getAllProducts({
|
||||
variables: { first: 100, entityIds },
|
||||
if (ids?.length) {
|
||||
const graphqlData = await commerce.getAllProducts({
|
||||
variables: { first: 100, ids },
|
||||
config,
|
||||
})
|
||||
// Put the products in an object that we can use to get them by id
|
||||
|
@ -1,4 +1,4 @@
|
||||
import type { ProductNode } from '../../product/get-all-products'
|
||||
import type { ProductNode } from '../operations/get-all-products'
|
||||
import type { RecursivePartial } from './types'
|
||||
|
||||
export default function setProductLocaleMeta(
|
||||
|
@ -1,135 +0,0 @@
|
||||
import type {
|
||||
GetAllProductsQuery,
|
||||
GetAllProductsQueryVariables,
|
||||
} from '../schema'
|
||||
import type { Product } from '@commerce/types'
|
||||
import type { RecursivePartial, RecursiveRequired } from '../api/utils/types'
|
||||
import filterEdges from '../api/utils/filter-edges'
|
||||
import setProductLocaleMeta from '../api/utils/set-product-locale-meta'
|
||||
import { productConnectionFragment } from '../api/fragments/product'
|
||||
import { BigcommerceConfig, getConfig } from '../api'
|
||||
import { normalizeProduct } from '../lib/normalize'
|
||||
|
||||
export const getAllProductsQuery = /* GraphQL */ `
|
||||
query getAllProducts(
|
||||
$hasLocale: Boolean = false
|
||||
$locale: String = "null"
|
||||
$entityIds: [Int!]
|
||||
$first: Int = 10
|
||||
$products: Boolean = false
|
||||
$featuredProducts: Boolean = false
|
||||
$bestSellingProducts: Boolean = false
|
||||
$newestProducts: Boolean = false
|
||||
) {
|
||||
site {
|
||||
products(first: $first, entityIds: $entityIds) @include(if: $products) {
|
||||
...productConnnection
|
||||
}
|
||||
featuredProducts(first: $first) @include(if: $featuredProducts) {
|
||||
...productConnnection
|
||||
}
|
||||
bestSellingProducts(first: $first) @include(if: $bestSellingProducts) {
|
||||
...productConnnection
|
||||
}
|
||||
newestProducts(first: $first) @include(if: $newestProducts) {
|
||||
...productConnnection
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
${productConnectionFragment}
|
||||
`
|
||||
|
||||
export type ProductEdge = NonNullable<
|
||||
NonNullable<GetAllProductsQuery['site']['products']['edges']>[0]
|
||||
>
|
||||
|
||||
export type ProductNode = ProductEdge['node']
|
||||
|
||||
export type GetAllProductsResult<
|
||||
T extends Record<keyof GetAllProductsResult, any[]> = {
|
||||
products: ProductEdge[]
|
||||
}
|
||||
> = T
|
||||
|
||||
const FIELDS = [
|
||||
'products',
|
||||
'featuredProducts',
|
||||
'bestSellingProducts',
|
||||
'newestProducts',
|
||||
]
|
||||
|
||||
export type ProductTypes =
|
||||
| 'products'
|
||||
| 'featuredProducts'
|
||||
| 'bestSellingProducts'
|
||||
| 'newestProducts'
|
||||
|
||||
export type ProductVariables = { field?: ProductTypes } & Omit<
|
||||
GetAllProductsQueryVariables,
|
||||
ProductTypes | 'hasLocale'
|
||||
>
|
||||
|
||||
async function getAllProducts(opts?: {
|
||||
variables?: ProductVariables
|
||||
config?: BigcommerceConfig
|
||||
preview?: boolean
|
||||
}): Promise<{ products: Product[] }>
|
||||
|
||||
async function getAllProducts<
|
||||
T extends Record<keyof GetAllProductsResult, any[]>,
|
||||
V = any
|
||||
>(opts: {
|
||||
query: string
|
||||
variables?: V
|
||||
config?: BigcommerceConfig
|
||||
preview?: boolean
|
||||
}): Promise<GetAllProductsResult<T>>
|
||||
|
||||
async function getAllProducts({
|
||||
query = getAllProductsQuery,
|
||||
variables: { field = 'products', ...vars } = {},
|
||||
config,
|
||||
}: {
|
||||
query?: string
|
||||
variables?: ProductVariables
|
||||
config?: BigcommerceConfig
|
||||
preview?: boolean
|
||||
// TODO: fix the product type here
|
||||
} = {}): Promise<{ products: Product[] | any[] }> {
|
||||
config = getConfig(config)
|
||||
|
||||
const locale = vars.locale || config.locale
|
||||
const variables: GetAllProductsQueryVariables = {
|
||||
...vars,
|
||||
locale,
|
||||
hasLocale: !!locale,
|
||||
}
|
||||
|
||||
if (!FIELDS.includes(field)) {
|
||||
throw new Error(
|
||||
`The field variable has to match one of ${FIELDS.join(', ')}`
|
||||
)
|
||||
}
|
||||
|
||||
variables[field] = true
|
||||
|
||||
// RecursivePartial forces the method to check for every prop in the data, which is
|
||||
// required in case there's a custom `query`
|
||||
const { data } = await config.fetch<RecursivePartial<GetAllProductsQuery>>(
|
||||
query,
|
||||
{ variables }
|
||||
)
|
||||
const edges = data.site?.[field]?.edges
|
||||
const products = filterEdges(edges as RecursiveRequired<typeof edges>)
|
||||
|
||||
if (locale && config.applyLocale) {
|
||||
products.forEach((product: RecursivePartial<ProductEdge>) => {
|
||||
if (product.node) setProductLocaleMeta(product.node)
|
||||
})
|
||||
}
|
||||
|
||||
return { products: products.map(({ node }) => normalizeProduct(node as any)) }
|
||||
}
|
||||
|
||||
export default getAllProducts
|
@ -1,4 +1,4 @@
|
||||
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 './get-all-products'
|
||||
export { default as getAllProducts } from '../api/operations/get-all-products'
|
||||
|
@ -1,6 +1,6 @@
|
||||
import * as Core from '@commerce/types/wishlist'
|
||||
import { definitions } from '../api/definitions/wishlist'
|
||||
import type { ProductEdge } from '../product/get-all-products'
|
||||
import type { ProductEdge } from '../api/operations/get-all-products'
|
||||
|
||||
export * from '@commerce/types/wishlist'
|
||||
|
||||
|
@ -3,7 +3,10 @@ import type { LoginOperation } from '../types/login'
|
||||
import type { GetAllPagesOperation, GetPageOperation } from '../types/page'
|
||||
import type { GetSiteInfoOperation } from '../types/site'
|
||||
import type { GetCustomerWishlistOperation } from '../types/wishlist'
|
||||
import type { GetAllProductPathsOperation } from '../types/product'
|
||||
import type {
|
||||
GetAllProductPathsOperation,
|
||||
GetAllProductsOperation,
|
||||
} from '../types/product'
|
||||
import type { APIProvider, CommerceAPI } from '.'
|
||||
|
||||
const noop = () => {
|
||||
@ -109,6 +112,22 @@ export type Operations<P extends APIProvider> = {
|
||||
} & OperationOptions
|
||||
): Promise<T['data']>
|
||||
}
|
||||
|
||||
getAllProducts: {
|
||||
<T extends GetAllProductsOperation>(opts: {
|
||||
variables?: T['variables']
|
||||
config?: P['config']
|
||||
preview?: boolean
|
||||
}): Promise<T['data']>
|
||||
|
||||
<T extends GetAllProductsOperation>(
|
||||
opts: {
|
||||
variables?: T['variables']
|
||||
config?: P['config']
|
||||
preview?: boolean
|
||||
} & OperationOptions
|
||||
): Promise<T['data']>
|
||||
}
|
||||
}
|
||||
|
||||
export type APIOperations<P extends APIProvider> = {
|
||||
|
@ -72,3 +72,12 @@ export type GetAllProductPathsOperation<
|
||||
data: { products: Pick<T['product'], 'path'>[] }
|
||||
variables: { first?: number }
|
||||
}
|
||||
|
||||
export type GetAllProductsOperation<T extends ProductTypes = ProductTypes> = {
|
||||
data: { products: T['product'][] }
|
||||
variables: {
|
||||
relevance?: 'featured' | 'best_selling' | 'newest'
|
||||
ids?: string[]
|
||||
first?: number
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,6 @@ import { ProductCard } from '@components/product'
|
||||
import type { GetStaticPropsContext, InferGetStaticPropsType } from 'next'
|
||||
|
||||
import { getConfig } from '@framework/api'
|
||||
import getAllProducts from '@framework/product/get-all-products'
|
||||
|
||||
export async function getStaticProps({
|
||||
preview,
|
||||
@ -14,7 +13,7 @@ export async function getStaticProps({
|
||||
}: GetStaticPropsContext) {
|
||||
const config = getConfig({ locale })
|
||||
|
||||
const { products } = await getAllProducts({
|
||||
const { products } = await commerce.getAllProducts({
|
||||
variables: { first: 12 },
|
||||
config,
|
||||
preview,
|
||||
|
Loading…
x
Reference in New Issue
Block a user