From 30fbfe548f042a267e63093021a9c1b788810b95 Mon Sep 17 00:00:00 2001
From: christian-calabrese <christian.calabrese@pagopa.it>
Date: Tue, 29 Oct 2024 16:33:10 +0100
Subject: [PATCH] [CAI-242] - Keep lambda warm by invoking it every 3 minutes
 (#1215)

* feat: keep lambda warm by invoking it every 3 minutes

* fix: add missing permissions to lambda

* chore: add changeset

* fix: eslint cloudfront functions
---
 .changeset/dull-items-grow.md                 |  5 ++++
 .../src/viewer-request-handler.ts             | 12 ++++++---
 .../src/modules/chatbot/data.tf               |  7 ++++-
 .../src/modules/chatbot/lambda_chatbot.tf     | 27 +++++++++++++++++++
 4 files changed, 46 insertions(+), 5 deletions(-)
 create mode 100644 .changeset/dull-items-grow.md

diff --git a/.changeset/dull-items-grow.md b/.changeset/dull-items-grow.md
new file mode 100644
index 0000000000..a9398add08
--- /dev/null
+++ b/.changeset/dull-items-grow.md
@@ -0,0 +1,5 @@
+---
+"infrastructure": patch
+---
+
+Chatbot lambda is now kept warm by invoking it every 3 minutes
diff --git a/apps/cloudfront-functions/src/viewer-request-handler.ts b/apps/cloudfront-functions/src/viewer-request-handler.ts
index bcca874095..bf58211057 100644
--- a/apps/cloudfront-functions/src/viewer-request-handler.ts
+++ b/apps/cloudfront-functions/src/viewer-request-handler.ts
@@ -20,17 +20,21 @@ const handler = (
     const isHomepage = uri === '/';
 
     // List of special cases (add more as needed)
-    const specialCases: string[] = ['.bollo'];
+    const specialCases: readonly string[] = ['.bollo'];
 
     // Function to check if URI ends with any of the special cases
-    const isSpecialCase = specialCases.some(caseExt => uri.endsWith(caseExt));
+    const isSpecialCase = specialCases.some((caseExt) => uri.endsWith(caseExt));
 
     if (!isHomepage) {
       if (uriEndsWithSlash) {
         request.uri = uri.replace(/\/$/, '');
       }
       // Always add .html if there's no file extension, including special cases
-      if (!isGitbookAssets && !isWoff2 && (!/\.[a-zA-Z]+$/.test(uri) || isSpecialCase)) {
+      if (
+        !isGitbookAssets &&
+        !isWoff2 &&
+        (!/\.[a-zA-Z]+$/.test(uri) || isSpecialCase)
+      ) {
         request.uri += '.html';
       }
     }
@@ -40,4 +44,4 @@ const handler = (
     // do nothing
     return event.request;
   }
-};
\ No newline at end of file
+};
diff --git a/apps/infrastructure/src/modules/chatbot/data.tf b/apps/infrastructure/src/modules/chatbot/data.tf
index 5d2cdb431d..3e20bd5788 100644
--- a/apps/infrastructure/src/modules/chatbot/data.tf
+++ b/apps/infrastructure/src/modules/chatbot/data.tf
@@ -44,7 +44,12 @@ data "aws_iam_policy_document" "lambda_dynamodb_policy" {
       "dynamodb:UpdateItem",
       "dynamodb:GetRecords"
     ]
-    resources = [module.dynamodb_chatbot_queries.dynamodb_table_arn, module.dynamodb_chatbot_sessions.dynamodb_table_arn]
+    resources = [
+      module.dynamodb_chatbot_queries.dynamodb_table_arn,
+      "${module.dynamodb_chatbot_queries.dynamodb_table_arn}/*",
+      module.dynamodb_chatbot_sessions.dynamodb_table_arn,
+      "${module.dynamodb_chatbot_sessions.dynamodb_table_arn}/*"
+    ]
   }
 }
 
diff --git a/apps/infrastructure/src/modules/chatbot/lambda_chatbot.tf b/apps/infrastructure/src/modules/chatbot/lambda_chatbot.tf
index 0e3e15e68e..a2e753ad59 100644
--- a/apps/infrastructure/src/modules/chatbot/lambda_chatbot.tf
+++ b/apps/infrastructure/src/modules/chatbot/lambda_chatbot.tf
@@ -104,3 +104,30 @@ module "index_id_ssm_parameter" {
   secure_type          = true
   ignore_value_changes = true
 }
+
+# Invoke the lambda function every 3 minutes from 6:00 am to 11:00 pm to keep it warm
+resource "aws_cloudwatch_event_rule" "lambda_invocation_rule" {
+  name                = "${local.prefix}-lambda-invocation-rule"
+  schedule_expression = "cron(0/3 6-23 * * ? *)"
+}
+
+resource "aws_cloudwatch_event_target" "lambda_target" {
+  rule      = aws_cloudwatch_event_rule.lambda_invocation_rule.name
+  target_id = "keep-chatbot-lambda-warm"
+  arn       = module.lambda_function.lambda_function_arn
+  input = jsonencode({
+    resource                        = "/",
+    path                            = "/",
+    httpMethod                      = "OPTIONS",
+    requestContext                  = {},
+    multiValueQueryStringParameters = null
+  })
+}
+
+resource "aws_lambda_permission" "allow_eventbridge" {
+  action        = "lambda:InvokeFunction"
+  function_name = module.lambda_function.lambda_function_name
+  principal     = "events.amazonaws.com"
+  source_arn    = aws_cloudwatch_event_rule.lambda_invocation_rule.arn
+  statement_id  = "AllowExecutionFromEventBridge"
+}
\ No newline at end of file