Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Allow typescript handler #176

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ Most options are set under `custom.warmup` in the `serverless.yaml` file.

* **folderName** (default `_warmup`)
* **cleanFolder** (default `true`)
* **tsHandler** (default `false`)
* **name** (default `${service}-${stage}-warmup-plugin`)
* **role** (default to role in the provider)
* **tags** (default to serverless default tags)
Expand All @@ -62,6 +63,7 @@ custom:
warmup:
enabled: true # Whether to warm up functions by default or not
folderName: '_warmup' # Name of the folder created for the generated warmup
tsHandler: true # Whether the handler function for warmup should be generated in TS or not
cleanFolder: false
memorySize: 256
name: 'make-them-pop'
Expand Down
76 changes: 71 additions & 5 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ class WarmUp {
this.functionsToWarmup,
this.resolvedOptions.region,
this.warmupOpts.pathFile,
this.warmupOpts.tsHandler,
);
WarmUp.addWarmUpFunctionToService(this.serverless.service, this.warmupOpts);
}
Expand Down Expand Up @@ -140,6 +141,7 @@ class WarmUp {
const config = (typeof possibleConfig === 'object') ? possibleConfig : {};
const folderName = (typeof config.folderName === 'string') ? config.folderName : '_warmup';
const pathFolder = path.join(this.serverless.config.servicePath, folderName);
const tsHandler = (typeof config.tsHandler === 'boolean') ? config.tsHandler : defaultOpts.tsHandler;

/* eslint-disable no-nested-ternary */
// Keep backwards compatibility for now
Expand All @@ -152,7 +154,8 @@ class WarmUp {
return {
folderName,
pathFolder,
pathFile: `${pathFolder}/index.js`,
tsHandler,
pathFile: `${pathFolder}/index.${tsHandler ? 'ts' : 'js'}`,
pathHandler: `${folderName}/index.warmUp`,
cleanFolder: (typeof config.cleanFolder === 'boolean') ? config.cleanFolder : defaultOpts.cleanFolder,
name: (typeof config.name === 'string') ? config.name : defaultOpts.name,
Expand Down Expand Up @@ -230,6 +233,7 @@ class WarmUp {
const globalDefaultOpts = {
folderName: '_warmup',
cleanFolder: true,
tsHandler: false,
memorySize: 128,
name: `${service.service}-${stage}-warmup-plugin`,
events: [{ schedule: 'rate(5 minutes)' }],
Expand Down Expand Up @@ -299,14 +303,26 @@ class WarmUp {
*
* @return {Promise}
* */
async createWarmUpFunctionArtifact(functions, region, pathFile) {
async createWarmUpFunctionArtifact(functions, region, pathFile, tsHandler) {
/** Log warmup start */
this.serverless.cli.log(`WarmUp: setting ${functions.length} lambdas to be warm`);

/** Log functions being warmed up */
functions.forEach(func => this.serverless.cli.log(`WarmUp: ${func.name}`));

const warmUpFunction = `"use strict";
const warmUpFunction = tsHandler
? WarmUp.getTSWarmupFunction(region, functions)
: WarmUp.getJSWarmupFunction(region, functions);

/** Write warm up file */
return fs.outputFile(pathFile, warmUpFunction);
}

/**
* @description Write the handler in javascript
* */
static getJSWarmupFunction(region, functions) {
return `"use strict";

/** Generated by Serverless WarmUp Plugin at ${new Date().toISOString()} */
const aws = require("aws-sdk");
Expand Down Expand Up @@ -353,9 +369,59 @@ module.exports.warmUp = async (event, context) => {

console.log(\`Warm Up Finished with \${invokes.filter(r => !r).length} invoke errors\`);
}`;
}

/** Write warm up file */
return fs.outputFile(pathFile, warmUpFunction);
/**
* @description Write the handler in typescript
* */
static getTSWarmupFunction(region, functions) {
return `"use strict";

/** Generated by Serverless WarmUp Plugin at ${new Date().toISOString()} */
import aws from "aws-sdk";
aws.config.region = "${region}";
const lambda = new aws.Lambda();
const functions = ${JSON.stringify(functions)};

export const warmUp = async (event: any, context) => {
console.log("Warm Up Start");

const invokes = await Promise.all(functions.map(async (func) => {
let concurrency;
const functionConcurrency = process.env["WARMUP_CONCURRENCY_" + func.name.toUpperCase().replace(/-/g, '_')]

if (functionConcurrency) {
concurrency = parseInt(functionConcurrency);
console.log(\`Warming up function: \${func.name} with concurrency: \${concurrency} (from function-specific environment variable)\`);
} else if (process.env.WARMUP_CONCURRENCY) {
concurrency = parseInt(process.env.WARMUP_CONCURRENCY);
console.log(\`Warming up function: \${func.name} with concurrency: \${concurrency} (from global environment variable)\`);
} else {
concurrency = func.config.concurrency;
console.log(\`Warming up function: \${func.name} with concurrency: \${concurrency}\`);
}

const params = {
ClientContext: Buffer.from(\`{"custom":\${func.config.payload}}\`).toString('base64'),
FunctionName: func.name,
InvocationType: "RequestResponse",
LogType: "None",
Qualifier: process.env.SERVERLESS_ALIAS || "$LATEST",
Payload: func.config.payload
};

try {
await Promise.all(Array(concurrency).fill(0).map(async () => await lambda.invoke(params).promise()));
console.log(\`Warm Up Invoke Success: \${func.name}\`);
return true;
} catch (e) {
console.log(\`Warm Up Invoke Error: \${func.name}\`, e);
return false;
}
}));

console.log(\`Warm Up Finished with \${invokes.filter(r => !r).length} invoke errors\`);
}`;
}

/**
Expand Down