diff --git a/framework/spree/api/operations/get-all-product-paths.ts b/framework/spree/api/operations/get-all-product-paths.ts index b96f7008a..9dab1e2a3 100644 --- a/framework/spree/api/operations/get-all-product-paths.ts +++ b/framework/spree/api/operations/get-all-product-paths.ts @@ -1,18 +1,90 @@ -// import data from '../../data.json' +import type { + OperationContext, + OperationOptions, +} from '@commerce/api/operations' +import type { Product } from '@commerce/types/product' +import type { GetAllProductPathsOperation } from '@commerce/types/product' +import { requireConfigValue } from '@framework/isomorphic-config' +import type { IProductsSlugs, SpreeSdkVariables } from '@framework/types' +import getProductPath from '@framework/utils/get-product-path' +import type { SpreeApiConfig, SpreeApiProvider } from '..' -export type GetAllProductPathsResult = { - products: Array<{ path: string }> -} +export default function getAllProductPathsOperation({ + commerce, +}: OperationContext) { + async function getAllProductPaths< + T extends GetAllProductPathsOperation + >(opts?: { + variables?: T['variables'] + config?: Partial + }): Promise -export default function getAllProductPathsOperation() { - function getAllProductPaths(): Promise { - console.log('getAllProductPaths called.') + async function getAllProductPaths( + opts: { + variables?: T['variables'] + config?: Partial + } & OperationOptions + ): Promise - return Promise.resolve({ - // products: data.products.map(({ path }) => ({ path })), - // TODO: Return Storefront [{ path: '/long-sleeve-shirt' }, ...] from Spree products. Paths using product IDs are fine too. - products: [], - }) + async function getAllProductPaths({ + query, + variables: getAllProductPathsVariables = {}, + config: userConfig, + }: { + query?: string + variables?: T['variables'] + config?: Partial + } = {}): Promise { + console.info( + 'getAllProductPaths called. Configuration: ', + 'query: ', + query, + 'getAllProductPathsVariables: ', + getAllProductPathsVariables, + 'config: ', + userConfig + ) + + const productsCount = requireConfigValue( + 'lastUpdatedProductsPrerenderCount' + ) + + if (productsCount === 0) { + return { + products: [], + } + } + + const variables: SpreeSdkVariables = { + methodPath: 'products.list', + arguments: [ + { + fields: { + product: 'slug', + }, + per_page: productsCount, + }, + ], + } + + const config = commerce.getConfig(userConfig) + const { fetch: apiFetch } = config // TODO: Send config.locale to Spree. + + const { + data: { data: spreeSuccessResponse }, + } = await apiFetch<{ data: IProductsSlugs }, SpreeSdkVariables>( + '__UNUSED__', + { + variables, + } + ) + + const normalizedProductsPaths: Pick[] = + spreeSuccessResponse.data.map((spreeProduct) => ({ + path: getProductPath(spreeProduct), + })) + + return { products: normalizedProductsPaths } } return getAllProductPaths diff --git a/framework/spree/types/index.ts b/framework/spree/types/index.ts index 3ff71eaa1..86f5bcd97 100644 --- a/framework/spree/types/index.ts +++ b/framework/spree/types/index.ts @@ -88,3 +88,12 @@ export interface VariantAttr extends JsonApiDocument { backorderable: boolean } } + +export interface ProductSlugAttr extends JsonApiDocument { + attributes: { + slug: string + } +} +export interface IProductsSlugs extends JsonApiListResponse { + data: ProductSlugAttr[] +} diff --git a/framework/spree/utils/get-product-path.ts b/framework/spree/utils/get-product-path.ts new file mode 100644 index 000000000..ee5d0b0fe --- /dev/null +++ b/framework/spree/utils/get-product-path.ts @@ -0,0 +1,7 @@ +import type { ProductSlugAttr } from '@framework/types' + +const getProductPath = (partialSpreeProduct: ProductSlugAttr): string => { + return `/${partialSpreeProduct.attributes.slug}` +} + +export default getProductPath diff --git a/framework/spree/utils/normalize-product.ts b/framework/spree/utils/normalize-product.ts index f01fea089..83234e566 100644 --- a/framework/spree/utils/normalize-product.ts +++ b/framework/spree/utils/normalize-product.ts @@ -15,6 +15,7 @@ import createGetAbsoluteImageUrl from './create-get-absolute-image-url' import expandOptions from './expand-options' import getMediaGallery from './get-media-gallery' import { findIncludedOfType } from './find-json-api-documents' +import getProductPath from './get-product-path' const normalizeProduct = ( spreeSuccessResponse: JsonApiSingleResponse | JsonApiListResponse, @@ -82,7 +83,7 @@ const normalizeProduct = ( }) const slug = spreeProduct.attributes.slug - const path = `/${spreeProduct.attributes.slug}` + const path = getProductPath(spreeProduct) return { id: spreeProduct.id, diff --git a/framework/spree/utils/validate-products-prerender-count.ts b/framework/spree/utils/validate-products-prerender-count.ts new file mode 100644 index 000000000..024db1ea6 --- /dev/null +++ b/framework/spree/utils/validate-products-prerender-count.ts @@ -0,0 +1,21 @@ +const validateProductsPrerenderCount = (prerenderCount: unknown): number => { + let prerenderCountInteger: number + + if (typeof prerenderCount === 'string') { + prerenderCountInteger = parseInt(prerenderCount) + } else if (typeof prerenderCount === 'number') { + prerenderCountInteger = prerenderCount + } else { + throw new TypeError( + 'prerenderCount count must be a string containing a number or an integer.' + ) + } + + if (prerenderCountInteger < 0) { + throw new RangeError('prerenderCount must be non-negative.') + } + + return prerenderCountInteger +} + +export default validateProductsPrerenderCount