diff --git a/.changeset/early-kangaroos-accept.md b/.changeset/early-kangaroos-accept.md new file mode 100644 index 0000000000..1fdb775cf7 --- /dev/null +++ b/.changeset/early-kangaroos-accept.md @@ -0,0 +1,5 @@ +--- +"nextjs-website": patch +--- + +Fix UrlReplaceMap codec, simplify populate and cache the result diff --git a/.changeset/witty-socks-smell.md b/.changeset/witty-socks-smell.md new file mode 100644 index 0000000000..40a4093084 --- /dev/null +++ b/.changeset/witty-socks-smell.md @@ -0,0 +1,5 @@ +--- +"infrastructure": patch +--- + +Enabled active campaign syncer on prod diff --git a/apps/infrastructure/src/env/prod/terraform.tfvars b/apps/infrastructure/src/env/prod/terraform.tfvars index aeb66a0cb0..ab8c365eca 100644 --- a/apps/infrastructure/src/env/prod/terraform.tfvars +++ b/apps/infrastructure/src/env/prod/terraform.tfvars @@ -33,4 +33,4 @@ dns_domain_name_cms = { } create_chatbot = true -ac_integration_is_enabled = false \ No newline at end of file +ac_integration_is_enabled = true \ No newline at end of file diff --git a/apps/infrastructure/src/main.tf b/apps/infrastructure/src/main.tf index d425648b4a..e25a379427 100644 --- a/apps/infrastructure/src/main.tf +++ b/apps/infrastructure/src/main.tf @@ -158,6 +158,6 @@ module "active_campaign" { environment = var.environment tags = var.tags - cognito_user_pool = module.website.cognito_user_pool - webinar_subscriptions_ddb_stream_arn = module.website.webinar_subscriptions_ddb_stream_arn + cognito_user_pool = module.website.cognito_user_pool + webinar_subscriptions_ddb = module.website.webinar_subscriptions_ddb } diff --git a/apps/infrastructure/src/modules/active_campaign/eventbridge.tf b/apps/infrastructure/src/modules/active_campaign/eventbridge.tf index 8386b7a3b9..30264c8f75 100644 --- a/apps/infrastructure/src/modules/active_campaign/eventbridge.tf +++ b/apps/infrastructure/src/modules/active_campaign/eventbridge.tf @@ -1,7 +1,7 @@ # EventBridge Pipe for DynamoDB Stream -> SQS FIFO resource "aws_pipes_pipe" "dynamodb_to_sqs" { name = "${local.prefix}-webinar-subscriptions-pipe" - source = var.webinar_subscriptions_ddb_stream_arn + source = var.webinar_subscriptions_ddb.stream_arn target = aws_sqs_queue.fifo_queue.arn role_arn = aws_iam_role.pipes_role.arn diff --git a/apps/infrastructure/src/modules/active_campaign/iam.tf b/apps/infrastructure/src/modules/active_campaign/iam.tf index b2a043599d..68418c4af4 100644 --- a/apps/infrastructure/src/modules/active_campaign/iam.tf +++ b/apps/infrastructure/src/modules/active_campaign/iam.tf @@ -31,7 +31,7 @@ resource "aws_iam_policy" "pipes" { ] Effect = "Allow" Resource = [ - var.webinar_subscriptions_ddb_stream_arn + var.webinar_subscriptions_ddb.stream_arn ] }, { @@ -65,6 +65,17 @@ resource "aws_iam_policy" "lambda_policy" { policy = jsonencode({ Version = "2012-10-17", Statement = [ + { + Action = [ + "dynamodb:BatchGetItem", + "dynamodb:GetItem", + "dynamodb:Query", + ] + Effect = "Allow" + Resource = [ + var.webinar_subscriptions_ddb.arn + ] + }, { Action = ["sqs:ReceiveMessage", "sqs:DeleteMessage", "sqs:GetQueueAttributes"], Effect = "Allow", diff --git a/apps/infrastructure/src/modules/active_campaign/lambda_resync.tf b/apps/infrastructure/src/modules/active_campaign/lambda_resync.tf index fe646d0131..c6c1131f17 100644 --- a/apps/infrastructure/src/modules/active_campaign/lambda_resync.tf +++ b/apps/infrastructure/src/modules/active_campaign/lambda_resync.tf @@ -6,9 +6,10 @@ module "lambda_resync" { description = "Lambda function that resyncs Active Campaign failed events" environment_variables = { - AC_API_KEY_PARAM = module.active_campaign_api_key.ssm_parameter_name - AC_BASE_URL_PARAM = module.active_campaign_base_url.ssm_parameter_name - COGNITO_USER_POOL_ID = var.cognito_user_pool.id + AC_API_KEY_PARAM = module.active_campaign_api_key.ssm_parameter_name + AC_BASE_URL_PARAM = module.active_campaign_base_url.ssm_parameter_name + COGNITO_USER_POOL_ID = var.cognito_user_pool.id + DYNAMO_WEBINARS_TABLE_NAME = var.webinar_subscriptions_ddb.name } runtime = "nodejs20.x" diff --git a/apps/infrastructure/src/modules/active_campaign/variables.tf b/apps/infrastructure/src/modules/active_campaign/variables.tf index 5e3aaf7411..4628fbdf2e 100644 --- a/apps/infrastructure/src/modules/active_campaign/variables.tf +++ b/apps/infrastructure/src/modules/active_campaign/variables.tf @@ -39,7 +39,10 @@ variable "cognito_user_pool" { description = "The cognito user pool used to authenticate api calls" } -variable "webinar_subscriptions_ddb_stream_arn" { - type = string - description = "The ARN of the webinar subscriptions ddb stream" +variable "webinar_subscriptions_ddb" { + type = object({ + name = string + arn = string + stream_arn = string + }) } \ No newline at end of file diff --git a/apps/infrastructure/src/modules/chatbot/lambda_chatbot.tf b/apps/infrastructure/src/modules/chatbot/lambda_chatbot.tf index ce85cb71d6..3f6741724d 100644 --- a/apps/infrastructure/src/modules/chatbot/lambda_chatbot.tf +++ b/apps/infrastructure/src/modules/chatbot/lambda_chatbot.tf @@ -26,6 +26,7 @@ locals { CHB_GOOGLE_API_KEY = module.google_api_key_ssm_parameter.ssm_parameter_name CHB_QUERY_TABLE_PREFIX = local.prefix CHB_LLAMAINDEX_INDEX_ID = module.index_id_ssm_parameter.ssm_parameter_name + CHB_ENGINE_USE_ASYNC = "False" } } diff --git a/apps/infrastructure/src/modules/website/outputs.tf b/apps/infrastructure/src/modules/website/outputs.tf index a791b5b83a..b65727f170 100644 --- a/apps/infrastructure/src/modules/website/outputs.tf +++ b/apps/infrastructure/src/modules/website/outputs.tf @@ -24,6 +24,10 @@ output "cognito_user_pool" { sensitive = true } -output "webinar_subscriptions_ddb_stream_arn" { - value = module.dynamodb_webinar_subscriptions.dynamodb_table_stream_arn +output "webinar_subscriptions_ddb" { + value = { + name = module.dynamodb_webinar_subscriptions.dynamodb_table_id + arn = module.dynamodb_webinar_subscriptions.dynamodb_table_arn + stream_arn = module.dynamodb_webinar_subscriptions.dynamodb_table_stream_arn + } } \ No newline at end of file diff --git a/apps/nextjs-website/src/app/[productSlug]/guides/[...productGuidePage]/page.tsx b/apps/nextjs-website/src/app/[productSlug]/guides/[...productGuidePage]/page.tsx index e8d8e3b9de..0bcc1ae9e0 100644 --- a/apps/nextjs-website/src/app/[productSlug]/guides/[...productGuidePage]/page.tsx +++ b/apps/nextjs-website/src/app/[productSlug]/guides/[...productGuidePage]/page.tsx @@ -13,7 +13,7 @@ import { } from '@/helpers/metadata.helpers'; import GitBookTemplate from '@/components/templates/GitBookTemplate/GitBookTemplate'; import { productPageToBreadcrumbs } from '@/helpers/breadcrumbs.helpers'; -import { getGuidesProps, getUrlReplaceMapProps } from '@/lib/cmsApi'; +import { getGuidesProps, getCachedUrlReplaceMapProps } from '@/lib/cmsApi'; import { generateStructuredDataScripts } from '@/helpers/generateStructuredDataScripts.helpers'; import { breadcrumbItemByProduct, @@ -77,7 +77,7 @@ const Page = async ({ params }: { params: Params }) => { params?.productSlug, params?.productGuidePage ?? [''] ); - const urlReplaceMap = await getUrlReplaceMapProps(); + const urlReplaceMap = await getCachedUrlReplaceMapProps(); const { product, page, guide, version, versions, source, bannerLinks, seo } = guideProps; const props: ProductGuidePageProps = { diff --git a/apps/nextjs-website/src/app/solutions/[solutionSlug]/[...solutionSubPathSlugs]/page.tsx b/apps/nextjs-website/src/app/solutions/[solutionSlug]/[...solutionSubPathSlugs]/page.tsx index 59b5d5e5e1..882ca48fc9 100644 --- a/apps/nextjs-website/src/app/solutions/[solutionSlug]/[...solutionSubPathSlugs]/page.tsx +++ b/apps/nextjs-website/src/app/solutions/[solutionSlug]/[...solutionSubPathSlugs]/page.tsx @@ -6,7 +6,7 @@ import { getSolutionDetail, getSolutionSubPaths } from '@/lib/api'; import GitBookTemplate from '@/components/templates/GitBookTemplate/GitBookTemplate'; import { pageToBreadcrumbs } from '@/helpers/breadcrumbs.helpers'; import { ParseContentConfig } from 'gitbook-docs/parseContent'; -import { getSolutionsProps, getUrlReplaceMapProps } from '@/lib/cmsApi'; +import { getSolutionsProps, getCachedUrlReplaceMapProps } from '@/lib/cmsApi'; import { SolutionTemplateProps } from '@/components/templates/SolutionTemplate/SolutionTemplate'; import { generateStructuredDataScripts } from '@/helpers/generateStructuredDataScripts.helpers'; import { getItemFromPaths } from '@/helpers/structuredData.helpers'; @@ -60,7 +60,7 @@ const Page = async ({ params }: { params: Params }) => { params?.solutionSubPathSlugs ); - const urlReplaceMap = await getUrlReplaceMapProps(); + const urlReplaceMap = await getCachedUrlReplaceMapProps(); if (!solutionProps) { return null; } diff --git a/apps/nextjs-website/src/lib/cmsApi.ts b/apps/nextjs-website/src/lib/cmsApi.ts index 71201bffbe..ec22b82491 100644 --- a/apps/nextjs-website/src/lib/cmsApi.ts +++ b/apps/nextjs-website/src/lib/cmsApi.ts @@ -49,8 +49,10 @@ import { makeOverviewsProps } from '@/lib/strapi/makeProps/makeOverviews'; import { fetchTutorialListPages } from './strapi/fetches/fetchTutorialListPages'; import { makeTutorialListPagesProps } from './strapi/makeProps/makeTutorialListPages'; import { fetchUrlReplaceMap } from './strapi/fetches/fetchUrlReplaceMap'; -import build from 'next/dist/build'; -import { makeUrlReplaceMap } from './strapi/makeProps/makeUrlReplaceMap'; +import { + makeUrlReplaceMap, + UrlReplaceMap, +} from './strapi/makeProps/makeUrlReplaceMap'; // a BuildEnv instance ready to be used const buildEnv = pipe( @@ -152,6 +154,21 @@ export const getUrlReplaceMapProps = async () => { return staticUrlReplaceMap; }; +// eslint-disable-next-line functional/no-let +let cachedUrlReplaceMapProps: UrlReplaceMap = {}; // We need to use any[] because of the type issue makeGuide derived type are not statically defined +// eslint-disable-next-line functional/no-let +let areUrlReplaceMapCached = false; + +export const getCachedUrlReplaceMapProps = async () => { + if (!areUrlReplaceMapCached) { + // eslint-disable-next-line functional/no-expression-statements + cachedUrlReplaceMapProps = await getUrlReplaceMapProps(); + // eslint-disable-next-line functional/no-expression-statements + areUrlReplaceMapCached = true; + } + return cachedUrlReplaceMapProps; +}; + export const getApiDataListPagesProps = async () => { const { config: { FETCH_FROM_STRAPI: fetchFromStrapi }, diff --git a/apps/nextjs-website/src/lib/strapi/codecs/UrlReplaceMapCodec.ts b/apps/nextjs-website/src/lib/strapi/codecs/UrlReplaceMapCodec.ts index c50c01743f..431a0bcbc5 100644 --- a/apps/nextjs-website/src/lib/strapi/codecs/UrlReplaceMapCodec.ts +++ b/apps/nextjs-website/src/lib/strapi/codecs/UrlReplaceMapCodec.ts @@ -1,13 +1,25 @@ import * as t from 'io-ts/lib'; -import { GuideCodec } from './GuidesCodec'; import { NullToUndefinedCodec } from './NullToUndefinedCodec'; +const CustomGuideCodec = t.strict({ + attributes: t.strict({ + title: t.string, + slug: t.string, + product: t.strict({ + data: t.strict({ + attributes: t.strict({ + slug: t.string, + }), + }), + }), + }), +}); const UrlToGuideCodec = t.strict({ id: t.number, url: t.string, subPath: t.union([NullToUndefinedCodec, t.string]), guide: t.strict({ - data: t.union([NullToUndefinedCodec, GuideCodec]), + data: t.union([NullToUndefinedCodec, CustomGuideCodec]), }), }); diff --git a/apps/nextjs-website/src/lib/strapi/fetches/fetchUrlReplaceMap.ts b/apps/nextjs-website/src/lib/strapi/fetches/fetchUrlReplaceMap.ts index 1db7b7298f..529e0c5114 100644 --- a/apps/nextjs-website/src/lib/strapi/fetches/fetchUrlReplaceMap.ts +++ b/apps/nextjs-website/src/lib/strapi/fetches/fetchUrlReplaceMap.ts @@ -8,17 +8,7 @@ const makeStrapiUrlReplaceMapPopulate = () => urlToGuide: { populate: { guide: { - populate: [ - 'image', - 'mobileImage', - 'listItems', - 'versions', - 'bannerLinks.icon', - 'seo', - 'seo.metaSocial.image', - 'product.logo', - 'product.bannerLinks.icon', - ], + populate: ['product'], }, }, },