Skip to content

Commit

Permalink
Merge branch 'main' into feat/multiple-templates-rewrites-plp
Browse files Browse the repository at this point in the history
  • Loading branch information
eduardoformiga committed May 16, 2024
2 parents 1fe1f1e + b179064 commit e3520da
Show file tree
Hide file tree
Showing 9 changed files with 188 additions and 27 deletions.
2 changes: 1 addition & 1 deletion lerna.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "3.0.55",
"version": "3.0.57",
"npmClient": "yarn",
"command": {
"publish": {
Expand Down
12 changes: 0 additions & 12 deletions packages/core/.babelrc.js

This file was deleted.

1 change: 0 additions & 1 deletion packages/core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ A quick look at the top-level files and directories you'll see in a this NextJS
├── public
├── src
├── test
├── .babelrc.js
├── .editorconfig
├── .prettierignore
├── .prettierrrc
Expand Down
15 changes: 9 additions & 6 deletions packages/core/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import ThirdPartyScripts from 'app/components/ThirdPartyScripts'
import AnalyticsHandler from 'app/sdk/analytics'
import ErrorBoundary from 'app/sdk/error/ErrorBoundary'
import UIProvider from 'app/sdk/ui/UIProvider'
import { WebFonts } from 'src/customizations/src/GlobalOverrides'

// TODO: The path will probably change when overriding fonts in the future
import DefaultFont from 'app/styles/fonts'

// import GlobalSections from './components/cms/GlobalSections'

Expand Down Expand Up @@ -78,11 +80,12 @@ export default async function RootLayout({
<AnalyticsHandler />

<html>
<head>
{!process.env.DISABLE_3P_SCRIPTS && <ThirdPartyScripts />}
<WebFonts />
</head>
<body className="theme">
<head>{!process.env.DISABLE_3P_SCRIPTS && <ThirdPartyScripts />}</head>
{/**
* TODO: Later when overriding fonts we should use the font variable in CSS files
* https://nextjs.org/docs/app/api-reference/components/font#css-variables
*/}
<body className={`theme ${DefaultFont.className}`}>
<UIProvider>
<>
{/* <GlobalSections {...globalSections}>{children}</GlobalSections>*/}
Expand Down
10 changes: 10 additions & 0 deletions packages/core/app/styles/fonts/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Inter } from 'next/font/google'

const customFont = Inter({
display: 'swap',
variable: '--fs-font-inter',
weight: ['400', '500', '600', '700', '900'],
subsets: ['latin'], // Either define subsets or set preload to false https://nextjs.org/docs/messages/google-fonts-missing-subsets
})

export default customFont
4 changes: 2 additions & 2 deletions packages/core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@faststore/core",
"version": "3.0.55",
"version": "3.0.57",
"license": "MIT",
"repository": "vtex/faststore",
"browserslist": "supports es6-module and not dead",
Expand Down Expand Up @@ -62,7 +62,7 @@
"css-loader": "^6.7.1",
"deepmerge": "^4.3.1",
"draftjs-to-html": "^0.9.1",
"graphql": "^15.0.0",
"graphql": "^15.6.0",
"include-media": "^1.4.10",
"next": "^13.5.6",
"next-seo": "^6.4.0",
Expand Down
74 changes: 70 additions & 4 deletions packages/core/src/server/cms/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ import MissingContentError from 'src/sdk/error/MissingContentError'
import MultipleContentError from 'src/sdk/error/MultipleContentError'
import config from '../../../faststore.config'

type Cache<T> = {
[key: string]: { data: Array<T> }
}
type ExtraOptions = {
cmsClient?: ClientCMS
cache?: Cache<ContentData>
}

export type Options =
| Locator
| {
Expand Down Expand Up @@ -45,10 +53,68 @@ export const clientCMS = new ClientCMS({
tenant: config.api.storeId,
})

export const getCMSPage = async (options: Options) => {
return await (isLocator(options)
? clientCMS.getCMSPage(options).then((page) => ({ data: [page] }))
: clientCMS.getCMSPagesByContentType(options.contentType, options.filters))
/*
* This in memory cache exists because for each page (think category or department)
* we are fetching all the pages of the same content type from the headless CMS to
* find the one that matches the slug.
*
* So instead of making multiple request for the Headless CMS API for each page we make
* one for each content-type and reuse the results for the next page.
*
* Since we rebuild on a CMS publication the server will go away and will "invalidate"
* the cache
*/
const getCMSPageCache = {}

export const getCMSPage = async (
options: Options,
extraOptions?: ExtraOptions
) => {
const cmsClient = extraOptions?.cmsClient ?? clientCMS
const cache = extraOptions?.cache ?? getCMSPageCache

if (isLocator(options)) {
return await cmsClient
.getCMSPage(options)
.then((page) => ({ data: [page] }))
}

if (!cache[options.contentType]) {
const pages = []
let page = 1
const perPage = 10
const response = await cmsClient.getCMSPagesByContentType(
options.contentType,
{ ...options.filters, page: page, perPage }
)

pages.push(...response.data)

const totalPagesToFetch = Math.ceil(response.totalItems / perPage) // How many pages have content
const pagesToFetch = Array.from(
{ length: totalPagesToFetch - 1 }, // We want all those pages minus the first one that we fetched
(_, i) => i + 2 // + 1 because indices are 0 based, and + 1 because we already fetched the first
)

if (response.totalItems > pages.length) {
const restOfPages = await Promise.all(
pagesToFetch.map((i) =>
cmsClient.getCMSPagesByContentType(options.contentType, {
...options.filters,
page: i,
perPage,
})
)
)

restOfPages.forEach((response) => {
pages.push(...response.data)
})
}
cache[options.contentType] = { data: pages }
}

return cache[options.contentType]
}

export const getPage = async <T extends ContentData>(options: Options) => {
Expand Down
95 changes: 95 additions & 0 deletions packages/core/test/server/cms/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { clientCMS, getCMSPage } from '../../../src/server/cms'
import { jest } from '@jest/globals'
import { ContentData } from '@vtex/client-cms'

describe('CMS Integration', () => {
const mockData = (count = 1) => {
const data: ContentData[] = []
for (let i = 0; i < count; i = i + 1) {
data.push({
id: `data-id-${i}`,
name: `data-name-${i}`,
status: `data-status-${i}`,
type: `data-type-${i}`,
sections: [],
releaseId: `release-${i}`,
})
}
return data
}

describe('getCMSPage', () => {
it('returns the first page if there is only one page', async () => {
const mockFunction = jest.fn(() => {
return Promise.resolve({
data: mockData(3),
hasNextPage: false,
totalItems: 3,
})
})
clientCMS.getCMSPagesByContentType = mockFunction

const result = await getCMSPage(
{ contentType: 'plp' },
{ cmsClient: clientCMS }
)

expect(mockFunction.mock.calls.length).toBe(1)
expect(result.data.length).toBe(3)
})

it('loads multiple pages', async () => {
const mockFunction: jest.Mock<typeof clientCMS.getCMSPagesByContentType> =
jest.fn()

mockFunction.mockImplementationOnce(() => {
return Promise.resolve({
data: mockData(10),
hasNextPage: true,
totalItems: 15,
})
})
mockFunction.mockImplementationOnce(() => {
return Promise.resolve({
data: mockData(5),
hasNextPage: false,
totalItems: 15,
})
})

clientCMS.getCMSPagesByContentType = mockFunction

const result = await getCMSPage(
{ contentType: 'plp' },
{ cmsClient: clientCMS, cache: {} }
)

expect(mockFunction.mock.calls.length).toBe(2)
expect(result.data.length).toBe(15)
})

it('it makes no request if the cache is filled', async () => {
const mockFunction: jest.Mock<typeof clientCMS.getCMSPagesByContentType> =
jest.fn()

mockFunction.mockImplementationOnce(() => {
return Promise.resolve({
data: mockData(10),
hasNextPage: true,
totalItems: 15,
})
})

clientCMS.getCMSPagesByContentType = mockFunction

const cache = { plp: { data: [] } }
const result = await getCMSPage(
{ contentType: 'plp' },
{ cmsClient: clientCMS, cache: cache }
)

expect(mockFunction.mock.calls.length).toBe(0)
expect(result.data.length).toBe(0)
})
})
})
2 changes: 1 addition & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -9146,7 +9146,7 @@ graphql-ws@5.12.1:
resolved "https://registry.yarnpkg.com/graphql-ws/-/graphql-ws-5.12.1.tgz#c62d5ac54dbd409cc6520b0b39de374b3d59d0dd"
integrity sha512-umt4f5NnMK46ChM2coO36PTFhHouBrK9stWWBczERguwYrGnPNxJ9dimU6IyOBfOkC6Izhkg4H8+F51W/8CYDg==

graphql@^15.0.0, graphql@^15.6.0:
graphql@^15.6.0:
version "15.8.0"
resolved "https://registry.yarnpkg.com/graphql/-/graphql-15.8.0.tgz#33410e96b012fa3bdb1091cc99a94769db212b38"
integrity sha512-5gghUc24tP9HRznNpV2+FIoq3xKkj5dTQqf4v0CpdPbFVwFkWoxOM+o+2OC9ZSvjEMTjfmG9QT+gcvggTwW1zw==
Expand Down

0 comments on commit e3520da

Please sign in to comment.