diff --git a/@generated/graphql/index.ts b/@generated/graphql/index.ts index 1376419f..a1e9ba29 100644 --- a/@generated/graphql/index.ts +++ b/@generated/graphql/index.ts @@ -2767,6 +2767,7 @@ export type CollectionPageQueryQuery = { titleTemplate: string | null title: string | null description: string | null + siteUrl: string | null } | null } | null } @@ -2799,18 +2800,17 @@ export type ProductPageQueryQuery = { } export type ServerProductPageQueryQueryVariables = Exact<{ - id: Scalars['String'] + slug: Scalars['String'] }> export type ServerProductPageQueryQuery = { product: { - slug: string sku: string gtin: string name: string description: string id: string - seo: { title: string; description: string } + seo: { title: string; description: string; canonical: string } brand: { name: string } breadcrumbList: { itemListElement: Array<{ item: string; name: string; position: number }> @@ -2848,6 +2848,7 @@ export type HomePageQueryQuery = { title: string | null description: string | null titleTemplate: string | null + siteUrl: string | null } | null } | null } diff --git a/@generated/graphql/persisted.json b/@generated/graphql/persisted.json index 8e3d4199..03bf2570 100644 --- a/@generated/graphql/persisted.json +++ b/@generated/graphql/persisted.json @@ -1,10 +1,10 @@ { "ProductGalleryQuery": "query ProductGalleryQuery($first: Int!, $after: String!, $sort: StoreSort!, $term: String!, $selectedFacets: [IStoreSelectedFacet!]!) {\n search(\n first: $first\n after: $after\n sort: $sort\n term: $term\n selectedFacets: $selectedFacets\n ) {\n products {\n pageInfo {\n totalCount\n }\n edges {\n node {\n id: productID\n slug\n sku\n brand {\n brandName: name\n name\n }\n name\n gtin\n isVariantOf {\n productGroupID\n name\n }\n image {\n url\n alternateName\n }\n offers {\n lowPrice\n offers {\n availability\n price\n listPrice\n quantity\n seller {\n identifier\n }\n }\n }\n }\n }\n }\n facets {\n key\n label\n type\n values {\n label\n value\n selected\n quantity\n }\n }\n }\n}\n", - "CollectionPageQuery": "query CollectionPageQuery {\n site {\n siteMetadata {\n titleTemplate\n title\n description\n }\n }\n}\n", + "CollectionPageQuery": "query CollectionPageQuery {\n site {\n siteMetadata {\n titleTemplate\n title\n description\n siteUrl\n }\n }\n}\n", "ServerCollectionPageQuery": "query ServerCollectionPageQuery($slug: String!) {\n collection(slug: $slug) {\n seo {\n title\n description\n }\n breadcrumbList {\n itemListElement {\n item\n name\n position\n }\n }\n meta {\n selectedFacets {\n key\n value\n }\n }\n }\n}\n", "ProductPageQuery": "query ProductPageQuery {\n site {\n siteMetadata {\n title\n description\n titleTemplate\n siteUrl\n }\n }\n}\n", - "ServerProductPageQuery": "query ServerProductPageQuery($id: String!) {\n product(locator: [{key: \"id\", value: $id}]) {\n id: productID\n slug\n seo {\n title\n description\n }\n brand {\n name\n }\n sku\n gtin\n name\n description\n breadcrumbList {\n itemListElement {\n item\n name\n position\n }\n }\n image {\n url\n alternateName\n }\n offers {\n lowPrice\n highPrice\n priceCurrency\n offers {\n availability\n price\n priceValidUntil\n priceCurrency\n itemCondition\n seller {\n identifier\n }\n listPrice\n }\n }\n isVariantOf {\n productGroupID\n name\n }\n additionalProperty {\n propertyID\n name\n value\n valueReference\n }\n }\n}\n", - "HomePageQuery": "query HomePageQuery {\n site {\n siteMetadata {\n title\n description\n titleTemplate\n }\n }\n}\n", + "ServerProductPageQuery": "query ServerProductPageQuery($slug: String!) {\n product(locator: [{key: \"slug\", value: $slug}]) {\n id: productID\n seo {\n title\n description\n canonical\n }\n brand {\n name\n }\n sku\n gtin\n name\n description\n breadcrumbList {\n itemListElement {\n item\n name\n position\n }\n }\n image {\n url\n alternateName\n }\n offers {\n lowPrice\n highPrice\n priceCurrency\n offers {\n availability\n price\n priceValidUntil\n priceCurrency\n itemCondition\n seller {\n identifier\n }\n listPrice\n }\n }\n isVariantOf {\n productGroupID\n name\n }\n additionalProperty {\n propertyID\n name\n value\n valueReference\n }\n }\n}\n", + "HomePageQuery": "query HomePageQuery {\n site {\n siteMetadata {\n title\n description\n titleTemplate\n siteUrl\n }\n }\n}\n", "SearchPageQuery": "query SearchPageQuery {\n site {\n siteMetadata {\n titleTemplate\n title\n description\n }\n }\n}\n", "ValidateCartMutation": "mutation ValidateCartMutation($cart: IStoreCart!) {\n validateCart(cart: $cart) {\n order {\n orderNumber\n acceptedOffer {\n seller {\n identifier\n }\n quantity\n price\n listPrice\n itemOffered {\n sku\n name\n image {\n url\n alternateName\n }\n brand {\n name\n }\n isVariantOf {\n productGroupID\n name\n }\n gtin\n additionalProperty {\n propertyID\n name\n value\n valueReference\n }\n }\n }\n }\n messages {\n text\n status\n }\n }\n}\n", "BrowserProductQuery": "query BrowserProductQuery($locator: [IStoreSelectedFacet!]!) {\n product(locator: $locator) {\n id: productID\n sku\n name\n gtin\n description\n isVariantOf {\n productGroupID\n name\n }\n image {\n url\n alternateName\n }\n brand {\n name\n }\n offers {\n lowPrice\n offers {\n availability\n price\n listPrice\n seller {\n identifier\n }\n }\n }\n breadcrumbList {\n itemListElement {\n item\n name\n position\n }\n }\n additionalProperty {\n propertyID\n name\n value\n valueReference\n }\n }\n}\n", diff --git a/CHANGELOG.md b/CHANGELOG.md index a77d3d78..cf2ff8a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added +- 301 redirects when visiting old VTEX product routes ([#87](https://github.com/vtex-sites/gatsby.store/pull/87)) ### Changed diff --git a/cypress/integration/seo.test.js b/cypress/integration/seo.test.js index 48dfa85a..c8ab90ac 100644 --- a/cypress/integration/seo.test.js +++ b/cypress/integration/seo.test.js @@ -7,7 +7,7 @@ */ import { options } from '../global' -import { cypress } from '../../store.config' +import { cypress, storeUrl } from '../../store.config' const { pages } = cypress @@ -35,7 +35,7 @@ describe('Home Page Seo', () => { cy.get('link[rel="canonical"]') .should('exist') .should(($link) => { - expect($link.attr('href')).to.eq(`https://${window.location.host}/`) + expect($link.attr('href')).to.eq(storeUrl) }) }) @@ -101,9 +101,7 @@ describe('Product Page Seo', () => { cy.get('link[rel="canonical"]') .should('exist') .should(($link) => { - expect($link.attr('href')).to.eq( - `https://${window.location.host}${pages.pdp}` - ) + expect($link.attr('href')).to.eq(`${storeUrl}${pages.pdp}`) }) }) @@ -193,9 +191,7 @@ describe('Collection Page Seo', () => { cy.get('link[rel="canonical"]') .should('exist') .should(($link) => { - expect($link.attr('href')).to.eq( - `https://${window.location.host}${pages.collection}/` - ) + expect($link.attr('href')).to.eq(`${storeUrl}${pages.collection}`) }) }) @@ -250,9 +246,7 @@ describe('Filtered Collection Page Seo', () => { cy.get('link[rel="canonical"]') .should('exist') .should(($link) => { - expect($link.attr('href')).to.eq( - `https://${window.location.host}${pages.collection}/` - ) + expect($link.attr('href')).to.eq(`${storeUrl}${pages.collection}`) }) }) diff --git a/package.json b/package.json index 0f3d170d..d63b3907 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "@envelop/graphql-jit": "^1.1.1", "@envelop/parser-cache": "^2.2.0", "@envelop/validation-cache": "^2.2.0", - "@faststore/api": "^1.9.6", + "@faststore/api": "^1.9.7", "@faststore/sdk": "^1.9.4", "@faststore/ui": "^1.9.4", "@vtex/graphql-utils": "^1.8.42", diff --git a/src/pages/[...slug].tsx b/src/pages/[...slug].tsx index f0906df7..e3198d39 100644 --- a/src/pages/[...slug].tsx +++ b/src/pages/[...slug].tsx @@ -57,7 +57,6 @@ function Page(props: Props) { const { data: { site }, serverData, - location: { host }, slug, } = props @@ -73,10 +72,7 @@ function Page(props: Props) { const { page } = searchParams const title = collection?.seo.title ?? site?.siteMetadata?.title ?? '' const pageQuery = page !== 0 ? `?page=${page}` : '' - const canonical = - host !== undefined - ? `https://${host}/${slug}/${pageQuery}` - : `/${slug}/${pageQuery}` + const canonical = `${site?.siteMetadata?.siteUrl}/${slug}${pageQuery}` return ( @@ -58,7 +57,7 @@ function Page(props: Props) { language={locale} openGraph={{ type: 'og:product', - url: `${site?.siteMetadata?.siteUrl}${slug}`, + url: canonical, title, description, images: product.image.map((img) => ({ @@ -131,21 +130,20 @@ export const querySSG = graphql` ` export const querySSR = gql` - query ServerProductPageQuery($id: String!) { - product(locator: [{ key: "id", value: $id }]) { + query ServerProductPageQuery($slug: String!) { + product(locator: [{ key: "slug", value: $slug }]) { id: productID - slug seo { title description + canonical } brand { name } - slug sku gtin name @@ -191,13 +189,11 @@ export const getServerData = async ({ params: Record }) => { const ONE_YEAR_CACHE = `s-maxage=31536000, stale-while-revalidate` - const id = slug.split('-').pop() - const { isNotFoundError } = await import('@faststore/api') const { execute } = await import('src/server/index') const { data, errors = [] } = await execute({ operationName: querySSR, - variables: { id }, + variables: { slug }, }) const notFound = errors.find(isNotFoundError) diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 6c94da64..1ef8521c 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -22,13 +22,12 @@ export type Props = PageProps function Page(props: Props) { const { data: { site }, - location: { pathname, host }, } = props const { locale } = useSession() const title = site?.siteMetadata?.title ?? '' - const siteUrl = `https://${host}${pathname}` + const siteUrl = `${site?.siteMetadata?.siteUrl}` return ( <> @@ -121,6 +120,7 @@ export const querySSG = graphql` title description titleTemplate + siteUrl } } } diff --git a/src/server/index.ts b/src/server/index.ts index d52bfc05..1676fdcb 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -1,17 +1,17 @@ /* eslint-disable react-hooks/rules-of-hooks */ import { envelop, + useAsyncSchema, useExtendContext, useMaskedErrors, - useAsyncSchema, } from '@envelop/core' -import type { FormatErrorHandler } from '@envelop/core' import { useGraphQlJit } from '@envelop/graphql-jit' import { useParserCache } from '@envelop/parser-cache' import { useValidationCache } from '@envelop/validation-cache' import { getContextFactory, getSchema, isFastStoreError } from '@faststore/api' -import type { Options as APIOptions } from '@faststore/api' import { GraphQLError } from 'graphql' +import type { FormatErrorHandler } from '@envelop/core' +import type { Options as APIOptions } from '@faststore/api' import persisted from '../../@generated/graphql/persisted.json' import storeConfig from '../../store.config' diff --git a/store.config.js b/store.config.js index 1b28ed95..c99b2233 100644 --- a/store.config.js +++ b/store.config.js @@ -25,7 +25,7 @@ module.exports = { server: process.env.BASE_SITE_URL || 'http://localhost:9000', pages: { home: '/', - pdp: '/apple-magic-mouse-99988212/p', + pdp: '/apple-magic-mouse/p', collection: '/office', }, }, @@ -34,7 +34,7 @@ module.exports = { cypress: { pages: { home: '/', - pdp: '/apple-magic-mouse-99988212/p', + pdp: '/apple-magic-mouse/p', collection: '/office', collection_filtered: '/office/?category-1=office&marca=acer&facets=category-1%2Cmarca', diff --git a/yarn.lock b/yarn.lock index 04be536e..6c5e0e80 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1974,10 +1974,10 @@ minimatch "^3.0.4" strip-json-comments "^3.1.1" -"@faststore/api@^1.9.6": - version "1.9.6" - resolved "https://registry.yarnpkg.com/@faststore/api/-/api-1.9.6.tgz#c94c32fc2fb2098d6016914475b508e3f9afcf42" - integrity sha512-J3zi7g+szhQBLU9jgYgFDwP4oQFJB+4BpFx/Qvtn6voRsScRoP1Sz9Yk/1lZ2t1T5IZFTn4Qw/oudK2ii5+hMA== +"@faststore/api@^1.9.7": + version "1.9.7" + resolved "https://registry.yarnpkg.com/@faststore/api/-/api-1.9.7.tgz#00c2ecad235a273860035c87e4be3b9c3dea4115" + integrity sha512-khpdJ7J4W2P9SZdF9cesfceEjrK0E0Z6xLd6hZmZEJXba2vhuvNHP1zgUYpFVkEawT1IBN7y0akLp7EygqPp5w== dependencies: "@graphql-tools/schema" "^8.2.0" "@rollup/plugin-graphql" "^1.0.0"