diff --git a/packages/api/src/index.ts b/packages/api/src/index.ts index 4c522a7e18..858cca83b4 100644 --- a/packages/api/src/index.ts +++ b/packages/api/src/index.ts @@ -8,6 +8,7 @@ import { typeDefs } from './typeDefs' import type { Options as OptionsVTEX } from './platforms/vtex' export * from './__generated__/schema' +export * from './platforms/errors' export type Options = OptionsVTEX diff --git a/packages/api/src/platforms/errors.ts b/packages/api/src/platforms/errors.ts new file mode 100644 index 0000000000..283d796164 --- /dev/null +++ b/packages/api/src/platforms/errors.ts @@ -0,0 +1,45 @@ +type ErrorType = 'BadRequestError' | 'NotFoundError' | 'RedirectError' + +interface Extension { + type: ErrorType + status: number +} + +class FastStoreError extends Error { + constructor(public extensions: T, message?: string) { + super(message) + this.name = 'FastStoreError' + } +} + +export class BadRequestError extends FastStoreError { + constructor(message?: string) { + super({ status: 400, type: 'BadRequestError' }, message) + } +} + +export class NotFoundError extends FastStoreError { + constructor(message?: string) { + super({ status: 404, type: 'NotFoundError' }, message) + } +} + +export class RedirectError extends FastStoreError< + Extension & { location: string; status: 301 | 302 } +> { + constructor(status: 301 | 302, location: string, message?: string) { + super({ status, location, type: 'RedirectError' }, message) + } +} + +export const isFastStoreError = (error: any): error is FastStoreError => + error?.name === 'FastStoreError' + +export const isRedirectError = (error: any): error is RedirectError => + error?.extensions?.type === 'RedirectError' + +export const isNotFoundError = (error: any): error is NotFoundError => + error?.extensions?.type === 'NotFoundError' + +export const isBadRequestError = (error: any): error is BadRequestError => + error?.extensions?.type === 'BadRequestError' diff --git a/packages/api/src/platforms/vtex/loaders/collection.ts b/packages/api/src/platforms/vtex/loaders/collection.ts index a1c66d9d10..32fe3ee2f4 100644 --- a/packages/api/src/platforms/vtex/loaders/collection.ts +++ b/packages/api/src/platforms/vtex/loaders/collection.ts @@ -1,7 +1,7 @@ import DataLoader from 'dataloader' import pLimit from 'p-limit' -import { NotFoundError } from '../utils/errors' +import { NotFoundError } from '../../errors' import type { CollectionPageType } from '../clients/commerce/types/Portal' import type { Options } from '..' import type { Clients } from '../clients' diff --git a/packages/api/src/platforms/vtex/resolvers/product.ts b/packages/api/src/platforms/vtex/resolvers/product.ts index abb59901f5..4b2d5b764c 100644 --- a/packages/api/src/platforms/vtex/resolvers/product.ts +++ b/packages/api/src/platforms/vtex/resolvers/product.ts @@ -41,10 +41,10 @@ export const StoreProduct: Record> & { name: ({ isVariantOf, name }) => name ?? isVariantOf.productName, slug: ({ isVariantOf: { linkText }, itemId }) => getSlug(linkText, itemId), description: ({ isVariantOf: { description } }) => description, - seo: ({ isVariantOf: { description, productName, linkText } }) => ({ + seo: ({ isVariantOf: { description, productName, linkText, items } }) => ({ title: productName, description, - canonical: `/${linkText}/p`, + canonical: `/${linkText}-${items[0].itemId}/p`, }), brand: ({ isVariantOf: { brand } }) => ({ name: brand }), breadcrumbList: ({ diff --git a/packages/api/src/platforms/vtex/resolvers/query.ts b/packages/api/src/platforms/vtex/resolvers/query.ts index e377a29cbd..59a8755793 100644 --- a/packages/api/src/platforms/vtex/resolvers/query.ts +++ b/packages/api/src/platforms/vtex/resolvers/query.ts @@ -1,6 +1,6 @@ +import { RedirectError, BadRequestError } from '../../errors' import { mutateChannelContext, mutateLocaleContext } from '../utils/contex' import { enhanceSku } from '../utils/enhanceSku' -import { BadRequestError } from '../utils/errors' import { findChannel, findLocale, @@ -55,13 +55,15 @@ export const Query = { // On standard VTEX PDP routes /slug/p does not contain sku ids. We need // to figure out the sku id before returning a product const products = await commerce.search.slug(slug) - const skuId = products?.[0]?.items[0]?.itemId + const product = products?.[0] + const skuId = product?.items[0]?.itemId + const location = skuId && `/${product.linkText}-${skuId}/p` if (skuId == null) { throw new BadRequestError(`Could not find product for slug ${slug}`) } - return skuLoader.load(skuId) + throw new RedirectError(301, location) } } diff --git a/packages/api/src/platforms/vtex/utils/errors.ts b/packages/api/src/platforms/vtex/utils/errors.ts deleted file mode 100644 index ddbcef8b28..0000000000 --- a/packages/api/src/platforms/vtex/utils/errors.ts +++ /dev/null @@ -1,13 +0,0 @@ -export class BadRequestError extends Error { - constructor(message: string) { - super(message) - this.name = 'BadRequestError' - } -} - -export class NotFoundError extends Error { - constructor(message: string) { - super(message) - this.name = 'NotFoundError' - } -}