From c15c9b9ba91b282a7778b130ebc8bae16da2f0ec Mon Sep 17 00:00:00 2001 From: Tanner Heffner Date: Fri, 29 Dec 2023 08:27:30 -0800 Subject: [PATCH 1/6] update query template to match use new helper functions --- generator-templates/query/query.hbs | 36 ++++++++--------------------- 1 file changed, 10 insertions(+), 26 deletions(-) diff --git a/generator-templates/query/query.hbs b/generator-templates/query/query.hbs index 9fe4dcd1c..70d9c6c80 100644 --- a/generator-templates/query/query.hbs +++ b/generator-templates/query/query.hbs @@ -4,6 +4,10 @@ import { queries } from '.' import { Node{{pascalCase name}} } from '@/types/drupal/node' import { {{pascalCase name}} } from '@/types/formatted/{{pascalCase name}}' import { ExpandedStaticPropsContext } from '@/lib/drupal/staticProps' +import { + entityBaseFields, + fetchSingleEntityOrPreview, +} from '@/lib/drupal/query' // Define the query params for fetching node--{{snakeCase name}}. export const params: QueryParams = () => { @@ -27,23 +31,11 @@ export type {{pascalCase name}}DataOpts = { export const data: QueryData<{{pascalCase name}}DataOpts, Node{{pascalCase name}}> = async ( opts ): Promise => { - const entity = opts?.context?.preview - ? // need to use getResourceFromContext for unpublished revisions - await drupalClient.getResourceFromContext( - 'node--{{snakeCase name}}', - opts.context, - { - params: params().getQueryObject(), - } - ) - : // otherwise just lookup by uuid - await drupalClient.getResource( - 'node--{{snakeCase name}}', - opts.id, - { - params: params().getQueryObject(), - } - ) +const entity = (await fetchSingleEntityOrPreview( + opts, + 'node--{{snakeCase name}}', + params + )) as Node{{pascalCase name}} return entity } @@ -52,14 +44,6 @@ export const formatter: QueryFormatter { return { - id: entity.id, - entityId: entity.drupal_internal__nid, - entityPath: entity.path.alias, - type: entity.type, - published: entity.status, - moderationState: entity.moderation_state, - title: entity.title, - metatags: entity.metatag, - breadcrumbs: entity.breadcrumbs + ...entityBaseFields(entity) } } From b312b3517de99f274b21cb43838c0947022359a2 Mon Sep 17 00:00:00 2001 From: Tanner Heffner Date: Fri, 29 Dec 2023 09:28:43 -0800 Subject: [PATCH 2/6] re-enable redis via env var --- package.json | 2 +- packages/env-loader/src/cli-options.ts | 1 + src/lib/drupal/drupalClient.ts | 4 +++- src/lib/drupal/query.ts | 7 ++++--- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 0d05e77e7..9eab1c985 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "build:preview": "yarn build:env-loader && node ./scripts/yarn/build:preview.js", "build:sitemap": "yarn build:env-loader & node ./scripts/yarn/build:sitemap.js", "start": "yarn build:env-loader && node ./scripts/yarn/start.js", - "export": "yarn build:env-loader && node ./scripts/yarn/export.js", + "export": "yarn build:env-loader && node ./scripts/yarn/export.js --USE_REDIS=true", "export:serve": "http-server out -p 8001", "redis": "docker run --name next-redis -p 6379:6379 -d redis", "redis:cli": "docker exec -it next-redis redis-cli", diff --git a/packages/env-loader/src/cli-options.ts b/packages/env-loader/src/cli-options.ts index 54531cd10..797cacc3d 100644 --- a/packages/env-loader/src/cli-options.ts +++ b/packages/env-loader/src/cli-options.ts @@ -78,6 +78,7 @@ export const getCliOptions = (scriptName: string): EnvVars => { .option('--DRUPAL_CLIENT_SECRET ', 'Drupal client secret') .option('--DRUPAL_PREVIEW_SECRET ', 'Drupal preview secret') .option('--DRUPAL_SITE_ID ', 'Drupal site ID') + .option('--USE_REDIS ', 'Enable redis') .option('--REDIS_URL ', 'Redis URL') .option('--SITE_URL ', 'Origin used for generated absolute paths') diff --git a/src/lib/drupal/drupalClient.ts b/src/lib/drupal/drupalClient.ts index 533534889..889c05625 100644 --- a/src/lib/drupal/drupalClient.ts +++ b/src/lib/drupal/drupalClient.ts @@ -13,6 +13,8 @@ export const drupalClient = new DrupalClient(baseUrl, { clientId: process.env.DRUPAL_CLIENT_ID, clientSecret: process.env.DRUPAL_CLIENT_SECRET, }, - // cache: redisCache(createRedisClient(process.env.REDIS_URL)), + cache: process.env.USE_REDIS + ? redisCache(createRedisClient(process.env.REDIS_URL)) + : null, previewSecret: process.env.DRUPAL_PREVIEW_SECRET, }) diff --git a/src/lib/drupal/query.ts b/src/lib/drupal/query.ts index f5b06dc59..049c48c83 100644 --- a/src/lib/drupal/query.ts +++ b/src/lib/drupal/query.ts @@ -1,8 +1,9 @@ import { DrupalJsonApiParams } from 'drupal-jsonapi-params' import { JsonApiResponse } from 'next-drupal' +import { PHASE_PRODUCTION_BUILD } from 'next/dist/shared/lib/constants' +import { QueryParams } from 'next-drupal-query' import { ResourceType } from '@/lib/constants/resourceTypes' import { drupalClient } from '@/lib/drupal/drupalClient' -import { QueryParams } from 'next-drupal-query' import { NodeTypes } from '@/types/drupal/node' import { PublishedEntity } from '@/types/formatted/publishedEntity' @@ -97,8 +98,8 @@ export async function getMenu(name: string, params: QueryParams) { params: params().getQueryObject(), // Cache resource during build, not dev. - // withCache: process.env.NEXT_PHASE === PHASE_PRODUCTION_BUILD, - // cacheKey: `menu:${name}`, + withCache: process.env.NEXT_PHASE === PHASE_PRODUCTION_BUILD, + cacheKey: `menu:${name}`, }) return menu From fb0a50b76579059f138b38931dfb33c9907bf76d Mon Sep 17 00:00:00 2001 From: Tanner Heffner Date: Fri, 29 Dec 2023 10:02:57 -0800 Subject: [PATCH 3/6] update generator template + comments --- generator-templates/query/query.hbs | 3 ++- plopfile.js | 15 +++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/generator-templates/query/query.hbs b/generator-templates/query/query.hbs index 70d9c6c80..05e064014 100644 --- a/generator-templates/query/query.hbs +++ b/generator-templates/query/query.hbs @@ -3,6 +3,7 @@ import { drupalClient } from '@/lib/drupal/drupalClient' import { queries } from '.' import { Node{{pascalCase name}} } from '@/types/drupal/node' import { {{pascalCase name}} } from '@/types/formatted/{{pascalCase name}}' +import { RESOURCE_TYPES } from '@/lib/constants/resourceTypes' import { ExpandedStaticPropsContext } from '@/lib/drupal/staticProps' import { entityBaseFields, @@ -33,7 +34,7 @@ export const data: QueryData<{{pascalCase name}}DataOpts, Node{{pascalCase name} ): Promise => { const entity = (await fetchSingleEntityOrPreview( opts, - 'node--{{snakeCase name}}', + RESOURCE_TYPES.{{constantCase name}}, params )) as Node{{pascalCase name}} diff --git a/plopfile.js b/plopfile.js index ef07a66ae..1fb6d479a 100644 --- a/plopfile.js +++ b/plopfile.js @@ -63,8 +63,11 @@ module.exports = function (plop) { templateFile: 'generator-templates/type/formatted.hbs', }, // Strings can be added to print a comment in the terminal. - 'You will need to manually import & add your query to src/data/queries/index.ts', - 'Be sure to also run `yarn test:u` to update test snapshots for your new query!', + 'You will need to do a few steps manually:', + '- Import & add your query to src/data/queries/index.ts', + '- Add your resource type to src/lib/constants/resourceTypes.ts', + '- Update the mock.json with correct data', + '- Run `yarn test:u` to update test snapshots for your new query!', ], }) @@ -101,8 +104,11 @@ module.exports = function (plop) { path: 'src/types/formatted/{{camelCase name}}.ts', templateFile: 'generator-templates/type/formatted.hbs', }, - 'You will need to manually import & add your query to src/data/queries/index.ts', - 'Be sure to also run `yarn test:u` to update test snapshots for your new query!', + 'You will need to do a few steps manually:', + '- Import & add your query to src/data/queries/index.ts', + '- Add your resource type to src/lib/constants/resourceTypes.ts', + '- Update the mock.json with correct data', + '- Run `yarn test:u` to update test snapshots for your new query!', // Create react component + test files for new Page type. { type: 'add', @@ -124,6 +130,7 @@ module.exports = function (plop) { path: 'playwright/tests/{{camelCase name}}.spec.js', templateFile: 'generator-templates/component/playwright.hbs', }, + 'To render your new page type, update [[...slug]].tsx', ], }) From a266a15a656ec6209dbe3c036b995eec5ad2e8ec Mon Sep 17 00:00:00 2001 From: Tanner Heffner Date: Fri, 29 Dec 2023 10:03:15 -0800 Subject: [PATCH 4/6] use RESOURCE_TYPES.FOO instead of hardcoded strings --- src/data/queries/event.ts | 3 ++- src/data/queries/headerFooter.ts | 11 +++-------- src/data/queries/newsStory.ts | 3 ++- src/data/queries/storyListing.ts | 19 ++++--------------- src/lib/drupal/drupalClient.ts | 1 + src/lib/drupal/query.ts | 18 ++++++++++++------ 6 files changed, 24 insertions(+), 31 deletions(-) diff --git a/src/data/queries/event.ts b/src/data/queries/event.ts index 5ba070928..9aa3070a0 100644 --- a/src/data/queries/event.ts +++ b/src/data/queries/event.ts @@ -13,6 +13,7 @@ import { entityBaseFields, fetchSingleEntityOrPreview, } from '@/lib/drupal/query' +import { RESOURCE_TYPES } from '@/lib/constants/resourceTypes' export const params: QueryParams = () => { return queries @@ -37,7 +38,7 @@ export const data: QueryData = async ( ): Promise => { const entity = (await fetchSingleEntityOrPreview( opts, - 'node--event', + RESOURCE_TYPES.EVENT, params )) as NodeEvent diff --git a/src/data/queries/headerFooter.ts b/src/data/queries/headerFooter.ts index 83b8d6be7..b87bcceb0 100644 --- a/src/data/queries/headerFooter.ts +++ b/src/data/queries/headerFooter.ts @@ -11,12 +11,7 @@ export type RawHeaderFooterData = { headerMegaMenu: HeaderMegaMenu } -// Define the query params for fetching footer menu data. -export const params: QueryParams = () => { - return queries.getParams().addFields('menu_items', ['title,url']) -} - -// Define the query params for fetching header megamenu data. Include referenced promo block data, if present +// Define extra equery params for fetching header megamenu data. Include referenced promo block data, if present export const megaMenuParams: QueryParams = () => { return queries .getParams() @@ -30,8 +25,8 @@ export const megaMenuParams: QueryParams = () => { export const data: QueryData = async () => { // Gather data from the different menus for the headerFooter data object - const footerColumns = await getMenu('va-gov-footer', params) - const footerBottomRail = await getMenu('footer-bottom-rail', params) + const footerColumns = await getMenu('va-gov-footer') + const footerBottomRail = await getMenu('footer-bottom-rail') const headerMegaMenu: HeaderMegaMenu = await getMenu( 'header-megamenu', megaMenuParams diff --git a/src/data/queries/newsStory.ts b/src/data/queries/newsStory.ts index ff9110963..6e741a728 100644 --- a/src/data/queries/newsStory.ts +++ b/src/data/queries/newsStory.ts @@ -7,6 +7,7 @@ import { entityBaseFields, fetchSingleEntityOrPreview, } from '@/lib/drupal/query' +import { RESOURCE_TYPES } from '@/lib/constants/resourceTypes' // Define the query params for fetching node--news_story. export const params: QueryParams = () => { @@ -33,7 +34,7 @@ export const data: QueryData = async ( ): Promise => { const entity = (await fetchSingleEntityOrPreview( opts, - 'node--news_story', + RESOURCE_TYPES.STORY, params )) as NodeNewsStory diff --git a/src/data/queries/storyListing.ts b/src/data/queries/storyListing.ts index 530208bf5..5bc334c35 100644 --- a/src/data/queries/storyListing.ts +++ b/src/data/queries/storyListing.ts @@ -1,10 +1,8 @@ import { QueryData, QueryFormatter, QueryParams } from 'next-drupal-query' -import { drupalClient } from '@/lib/drupal/drupalClient' import { queries } from '.' import { NodeStoryListing, NodeNewsStory } from '@/types/drupal/node' import { Menu } from '@/types/drupal/menu' import { StoryListing } from '@/types/formatted/storyListing' -import { DrupalJsonApiParams } from 'drupal-jsonapi-params' import { buildSideNavDataFromMenu } from '@/lib/drupal/facilitySideNav' import { ListingPageDataOpts } from '@/lib/drupal/listingPages' import { RESOURCE_TYPES } from '@/lib/constants/resourceTypes' @@ -14,6 +12,7 @@ import { fetchSingleResourceCollectionPage, entityBaseFields, fetchSingleEntityOrPreview, + getMenu, } from '@/lib/drupal/query' const PAGE_SIZE = PAGE_SIZES[RESOURCE_TYPES.STORY_LISTING] @@ -45,7 +44,7 @@ export const data: QueryData = async ( ) => { const entity = (await fetchSingleEntityOrPreview( opts, - 'node--story_listing', + RESOURCE_TYPES.STORY_LISTING, params )) as NodeStoryListing @@ -67,19 +66,9 @@ export const data: QueryData = async ( PAGE_SIZE ) - // Fetch facility menu (sidebar navigation) - const menuOpts = { - params: new DrupalJsonApiParams() - .addFields('menu_items', ['title,url']) - .getQueryObject(), - } - - // Fetch the menu name dynamically off of the field_office reference - // We may want to make our own version of this method, a la staticPathResources - const menu = await drupalClient.getMenu( + const menu = await getMenu( entity.field_office.field_system_menu.resourceIdObjMeta - .drupal_internal__target_id, - menuOpts + .drupal_internal__target_id ) return { diff --git a/src/lib/drupal/drupalClient.ts b/src/lib/drupal/drupalClient.ts index 889c05625..d23e88f4f 100644 --- a/src/lib/drupal/drupalClient.ts +++ b/src/lib/drupal/drupalClient.ts @@ -13,6 +13,7 @@ export const drupalClient = new DrupalClient(baseUrl, { clientId: process.env.DRUPAL_CLIENT_ID, clientSecret: process.env.DRUPAL_CLIENT_SECRET, }, + // Generally use cache for `yarn export` as it generates all pages cache: process.env.USE_REDIS ? redisCache(createRedisClient(process.env.REDIS_URL)) : null, diff --git a/src/lib/drupal/query.ts b/src/lib/drupal/query.ts index 049c48c83..458b030c9 100644 --- a/src/lib/drupal/query.ts +++ b/src/lib/drupal/query.ts @@ -1,7 +1,8 @@ import { DrupalJsonApiParams } from 'drupal-jsonapi-params' +import { PHASE_PRODUCTION_BUILD } from 'next/constants' import { JsonApiResponse } from 'next-drupal' -import { PHASE_PRODUCTION_BUILD } from 'next/dist/shared/lib/constants' import { QueryParams } from 'next-drupal-query' +import { queries } from '@/data/queries' import { ResourceType } from '@/lib/constants/resourceTypes' import { drupalClient } from '@/lib/drupal/drupalClient' import { NodeTypes } from '@/types/drupal/node' @@ -92,10 +93,15 @@ export async function fetchAndConcatAllResourceCollectionPages( } } -// Fetch drupal menu resource with cache -export async function getMenu(name: string, params: QueryParams) { +// Fetch drupal menu resource with cache. +export async function getMenu(name: string, params?: QueryParams) { + const defaultMenuParams = queries + .getParams() + .addFields('menu_items', ['title,url']) + .getQueryObject() + const menu = await drupalClient.getMenu(name, { - params: params().getQueryObject(), + params: params ? params().getQueryObject() : defaultMenuParams, // Cache resource during build, not dev. withCache: process.env.NEXT_PHASE === PHASE_PRODUCTION_BUILD, @@ -105,7 +111,7 @@ export async function getMenu(name: string, params: QueryParams) { return menu } -// Consistent handler to fetch a node entity from a normal route or a preview route +// Consistent handler to fetch a node entity from a normal route or a preview route. export async function fetchSingleEntityOrPreview(opts, type, params) { const entity = opts?.context?.preview ? // need to use getResourceFromContext for unpublished revisions @@ -120,7 +126,7 @@ export async function fetchSingleEntityOrPreview(opts, type, params) { return entity } -// Helper function to return a consistent set of base fields for resources +// Helper function to return a consistent set of base fields for resources. export const entityBaseFields = (entity: NodeTypes): PublishedEntity => { return { id: entity.id, From 7e14d0873888c0e3d24601e0e995e1e86a95c10a Mon Sep 17 00:00:00 2001 From: Tanner Heffner Date: Fri, 29 Dec 2023 10:05:27 -0800 Subject: [PATCH 5/6] keep comment about where menu comes from --- src/data/queries/storyListing.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/data/queries/storyListing.ts b/src/data/queries/storyListing.ts index 5bc334c35..ec2e94d8b 100644 --- a/src/data/queries/storyListing.ts +++ b/src/data/queries/storyListing.ts @@ -66,6 +66,7 @@ export const data: QueryData = async ( PAGE_SIZE ) + // Fetch the menu name dynamically off of the field_office reference const menu = await getMenu( entity.field_office.field_system_menu.resourceIdObjMeta .drupal_internal__target_id From b148cbd77f210da8175c6688e3665e7146886d4d Mon Sep 17 00:00:00 2001 From: Tanner Heffner Date: Tue, 2 Jan 2024 12:29:15 -0800 Subject: [PATCH 6/6] update --USE_REDIS flag to true boolean --- package.json | 2 +- packages/env-loader/src/cli-options.ts | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 9eab1c985..31c39e5ec 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "build:preview": "yarn build:env-loader && node ./scripts/yarn/build:preview.js", "build:sitemap": "yarn build:env-loader & node ./scripts/yarn/build:sitemap.js", "start": "yarn build:env-loader && node ./scripts/yarn/start.js", - "export": "yarn build:env-loader && node ./scripts/yarn/export.js --USE_REDIS=true", + "export": "yarn build:env-loader && node ./scripts/yarn/export.js --USE_REDIS", "export:serve": "http-server out -p 8001", "redis": "docker run --name next-redis -p 6379:6379 -d redis", "redis:cli": "docker exec -it next-redis redis-cli", diff --git a/packages/env-loader/src/cli-options.ts b/packages/env-loader/src/cli-options.ts index 797cacc3d..02ac5d9e3 100644 --- a/packages/env-loader/src/cli-options.ts +++ b/packages/env-loader/src/cli-options.ts @@ -56,6 +56,8 @@ const configureAdditionalHelpText = ( /** * Parses CLI options from the command line into an object. + * + * See https://www.npmjs.com/package/commander#common-option-types-boolean-and-value */ export const getCliOptions = (scriptName: string): EnvVars => { const program = new Command() @@ -78,7 +80,7 @@ export const getCliOptions = (scriptName: string): EnvVars => { .option('--DRUPAL_CLIENT_SECRET ', 'Drupal client secret') .option('--DRUPAL_PREVIEW_SECRET ', 'Drupal preview secret') .option('--DRUPAL_SITE_ID ', 'Drupal site ID') - .option('--USE_REDIS ', 'Enable redis') + .option('--USE_REDIS', 'Enable redis') .option('--REDIS_URL ', 'Redis URL') .option('--SITE_URL ', 'Origin used for generated absolute paths')