aws/deploy/deployPreLambda.js

/**
 * Exports an async function that deploys our pre queue Lambda, which is the Lambda triggered by the API Gateway resource "/beekeeper", i.e. first time visitors.
 * @module deployPreLambda
 */
const {
  LambdaClient,
  CreateFunctionCommand,
  AddPermissionCommand,
} = require("@aws-sdk/client-lambda");
const logger = require("../../utils/logger")("dev");
const fs = require("fs");

/**
 * Function that creates the pre-queue Lambda. This deployment sets several environment variables so that the preLambda code in its index.js has access to these environment variables. This pre Lambda will generate a token, put that token in the SQS queue, and set custom response headers dependent on the status of the environment variables.
 * @param {LambdaClient} lambda Looks like `new LambdaClient({ region })`
 * @param {String} lambdaName A constant `beekeeper-${PROFILE_NAME}-prelambda`
 * @param {String} sqsUrl URL of the sqs queue returned by `deploySQS.js`
 * @param {String} code This is the path of the index.js.zip file holding the code for this Lambda.
 * @param {String} roleArn Amazon resource number for the kitchen sink role returned by `createRole()`
 * @param {String} region A constant destructured from the CLI user's answers in `deploy.js`. Like "us-east-2".
 * @param {String} s3ObjectRootDomain Constant `https://${bucketName}.s3.${region}.amazonaws.com`;
 * @param {String} protectUrl A constant destructured from the CLI user's answers in `deploy.js`. Like "https://www.example.com".
 * @returns {String} Returns the ARN of this pre queue Lambda
 */
const createPreLambda = async (
  lambda,
  lambdaName,
  sqsUrl,
  code,
  roleArn,
  region,
  s3ObjectRootDomain,
  protectUrl
) => {
  const params = {
    FunctionName: lambdaName,
    Role: roleArn,
    Handler: "index.handler",
    Runtime: "nodejs12.x",
    Description: "producerLambda",
    Environment: {
      Variables: {
        SQS_URL: sqsUrl,
        REGION: region,
        S3_OBJECT_ROOT_DOMAIN: s3ObjectRootDomain,
        WAITROOM_ON: "true",
        PROTECT_URL: protectUrl
      },
    },
    Code: { ZipFile: code },
  };

  const command = new CreateFunctionCommand(params);

  try {
    const { FunctionArn } = await lambda.send(command);
    logger.debugSuccess(`Successfully created PreLambda: ${FunctionArn}`);
    return FunctionArn;
  } catch (err) {
    logger.debugError("Error", err);
    throw new Error(err);
  }
};

/**
 * Adds a permission so that the API Gateway can trigger the Lambda. 
 * @param {LambdaClient} lambda Looks like `new LambdaClient({ region })`
 * @param {String} lambdaName A constant `beekeeper-${PROFILE_NAME}-prelambda`
 */
const addLambdaPermission = async (lambda, lambdaName) => {
  let params = {
    Action: "lambda:InvokeFunction",
    FunctionName: lambdaName,
    Principal: "apigateway.amazonaws.com",
    StatementId: `${Math.random().toString(16).substring(2)}`,
  };
  const command = new AddPermissionCommand(params);

  try {
    await lambda.send(command);
    logger.debugSuccess(`Successfully added API Gateway permission to Lambda.`);
  } catch (err) {
    logger.debugError("Error", err);
    throw new Error(err);
  }
};

/**
 * 
 * @param {String} region A constant destructured from the CLI user's answers in `deploy.js`. Like "us-east-2".
 * @param {String} lambdaName A constant `beekeeper-${PROFILE_NAME}-prelambda`
 * @param {String} sqsUrl URL of the sqs queue returned by `deploySQS.js`
 * @param {String} asset This is the path of the index.js.zip file holding the code for this Lambda.
 * @param {String} roleArn Amazon resource number for the kitchen sink role returned by `createRole()`
 * @param {String} s3ObjectRootDomain Constant `https://${bucketName}.s3.${region}.amazonaws.com`;
 * @param {String} protectUrl A constant destructured from the CLI user's answers in `deploy.js`. Like "https://www.example.com".
 * @returns {String} Returns the ARN of this pre queue Lambda
 */
module.exports = async (
  region,
  lambdaName,
  sqsUrl,
  asset,
  roleArn,
  s3ObjectRootDomain,
  protectUrl
) => {
  let code = fs.readFileSync(asset);

  // Create a Lambda client service object
  const lambda = new LambdaClient({ region });

  // Create pre lambda
  const lambdaArn = await createPreLambda(
    lambda,
    lambdaName,
    sqsUrl,
    code,
    roleArn,
    region,
    s3ObjectRootDomain,
    protectUrl
  );

  // Add API Gateway Permission (Solution to AWS Bug)
  await addLambdaPermission(lambda, lambdaName);

  return lambdaArn;
};