From 2938825538c3fe20a9916c4f9db59395104f6241 Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Thu, 11 May 2023 19:41:48 +0200 Subject: [PATCH] style(commons): apply standardized formatting --- packages/commons/.eslintrc.js | 72 ++++++++++++ packages/commons/jest.config.js | 47 +++----- packages/commons/package.json | 9 +- packages/commons/src/Utility.ts | 52 ++++---- packages/commons/src/config/ConfigService.ts | 7 +- .../src/config/EnvironmentVariablesService.ts | 14 +-- packages/commons/src/config/index.ts | 2 +- packages/commons/src/index.ts | 2 +- .../samples/resources/contexts/hello-world.ts | 7 +- .../src/samples/resources/contexts/index.ts | 2 +- .../src/samples/resources/events/index.ts | 2 +- packages/commons/src/types/middy.ts | 49 +++++--- .../src/utils/lambda/LambdaInterface.ts | 10 +- packages/commons/src/utils/lambda/index.ts | 2 +- .../commons/tests/unit/InvocationLogs.test.ts | 68 +++++++---- .../tests/unit/LambdaInterface.test.ts | 92 ++++++++++----- packages/commons/tests/unit/Utility.test.ts | 35 ++---- .../EnvironmentVariablesService.test.ts | 74 +++++------- .../commons/tests/utils/InvocationLogs.ts | 25 ++-- packages/commons/tests/utils/cdk-cli.ts | 32 +++-- packages/commons/tests/utils/e2eUtils.ts | 111 +++++++++++------- 21 files changed, 424 insertions(+), 290 deletions(-) create mode 100644 packages/commons/.eslintrc.js diff --git a/packages/commons/.eslintrc.js b/packages/commons/.eslintrc.js new file mode 100644 index 0000000000..2e4ca18fb2 --- /dev/null +++ b/packages/commons/.eslintrc.js @@ -0,0 +1,72 @@ +module.exports = { + env: { + browser: false, + es2020: true, + jest: true, + node: true, + }, + ignorePatterns: ['coverage', 'lib'], + extends: [ + 'plugin:@typescript-eslint/recommended', + 'plugin:prettier/recommended', + ], + parser: '@typescript-eslint/parser', + plugins: ['@typescript-eslint', 'prettier'], + settings: { + 'import/resolver': { + node: {}, + typescript: { + project: './tsconfig.json', + alwaysTryTypes: true, + }, + }, + }, + rules: { + '@typescript-eslint/explicit-function-return-type': [ + 'error', + { allowExpressions: true }, + ], // Enforce return type definitions for functions + '@typescript-eslint/explicit-member-accessibility': 'error', // Enforce explicit accessibility modifiers on class properties and methods (public, private, protected) + '@typescript-eslint/member-ordering': [ + // Standardize the order of class members + 'error', + { + default: { + memberTypes: [ + 'signature', + 'public-field', + 'protected-field', + 'private-field', + 'constructor', + 'public-method', + 'protected-method', + 'private-method', + ], + order: 'alphabetically', + }, + }, + ], + '@typescript-eslint/no-explicit-any': 'error', // Disallow usage of the any type + '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }], // Disallow unused variables, except for variables starting with an underscore + '@typescript-eslint/no-use-before-define': ['off'], // Check if this rule is needed + 'no-unused-vars': 'off', // Disable eslint core rule, since it's replaced by @typescript-eslint/no-unused-vars + // Rules from eslint core https://eslint.org/docs/latest/rules/ + 'array-bracket-spacing': ['error', 'never'], // Disallow spaces inside of array brackets + 'computed-property-spacing': ['error', 'never'], // Disallow spaces inside of computed properties + 'func-style': ['warn', 'expression'], // Enforce function expressions instead of function declarations + 'keyword-spacing': 'error', // Enforce spaces after keywords and before parenthesis, e.g. if (condition) instead of if(condition) + 'padding-line-between-statements': [ + // Require an empty line before return statements + 'error', + { blankLine: 'always', prev: '*', next: 'return' }, + ], + 'no-console': 0, // Allow console.log statements + 'no-multi-spaces': ['error', { ignoreEOLComments: false }], // Disallow multiple spaces except for comments + 'no-multiple-empty-lines': ['error', { max: 1, maxBOF: 0, maxEOF: 0 }], // Enforce no empty line at the beginning & end of files and max 1 empty line between consecutive statements + 'no-throw-literal': 'error', // Disallow throwing literals as exceptions, e.g. throw 'error' instead of throw new Error('error') + 'object-curly-spacing': ['error', 'always'], // Enforce spaces inside of curly braces in objects + 'prefer-arrow-callback': 'error', // Enforce arrow functions instead of anonymous functions for callbacks + quotes: ['error', 'single', { allowTemplateLiterals: true }], // Enforce single quotes except for template strings + semi: ['error', 'always'], // Require semicolons instead of ASI (automatic semicolon insertion) at the end of statements + }, +}; diff --git a/packages/commons/jest.config.js b/packages/commons/jest.config.js index c60b4837dc..4e0793d3dc 100644 --- a/packages/commons/jest.config.js +++ b/packages/commons/jest.config.js @@ -3,37 +3,24 @@ module.exports = { name: 'AWS Lambda Powertools utility: COMMONS', color: 'red', }, - 'preset': 'ts-jest', - 'transform': { + preset: 'ts-jest', + transform: { '^.+\\.ts?$': 'ts-jest', }, - moduleFileExtensions: [ 'js', 'ts' ], - 'collectCoverageFrom': [ - '**/src/**/*.ts', - '!**/node_modules/**', - ], - 'testMatch': ['**/?(*.)+(spec|test).ts'], - 'roots': [ - '/src', - '/tests', - ], - 'testPathIgnorePatterns': [ - '/node_modules/', - ], - 'testEnvironment': 'node', - 'coveragePathIgnorePatterns': [ - '/node_modules/', - ], - 'coverageThreshold': { - 'global': { - 'statements': 100, - 'branches': 100, - 'functions': 100, - 'lines': 100, + moduleFileExtensions: ['js', 'ts'], + collectCoverageFrom: ['**/src/**/*.ts', '!**/node_modules/**'], + testMatch: ['**/?(*.)+(spec|test).ts'], + roots: ['/src', '/tests'], + testPathIgnorePatterns: ['/node_modules/'], + testEnvironment: 'node', + coveragePathIgnorePatterns: ['/node_modules/'], + coverageThreshold: { + global: { + statements: 100, + branches: 100, + functions: 100, + lines: 100, }, }, - 'coverageReporters': [ - 'json-summary', - 'text', - 'lcov' ], -}; \ No newline at end of file + coverageReporters: ['json-summary', 'text', 'lcov'], +}; diff --git a/packages/commons/package.json b/packages/commons/package.json index 17860e2384..17b3b436e6 100644 --- a/packages/commons/package.json +++ b/packages/commons/package.json @@ -16,14 +16,15 @@ "test:e2e": "echo 'Not Applicable'", "watch": "jest --watch", "build": "tsc", - "lint": "eslint --ext .ts --no-error-on-unmatched-pattern src tests", - "lint-fix": "eslint --fix --ext .ts --no-error-on-unmatched-pattern src tests", + "lint": "eslint --ext .ts,.js --no-error-on-unmatched-pattern .", + "lint-fix": "eslint --fix --ext .ts,.js --no-error-on-unmatched-pattern .", "package": "mkdir -p dist/ && npm pack && mv *.tgz dist/", "package-bundle": "../../package-bundler.sh commons-bundle ./dist", "prepare": "npm run build" }, "lint-staged": { - "*.ts": "npm run lint-fix" + "*.ts": "npm run lint-fix", + "*.js": "npm run lint-fix" }, "homepage": "https://github.com/awslabs/aws-lambda-powertools-typescript/tree/main/packages/metrics#readme", "license": "MIT-0", @@ -51,4 +52,4 @@ "@aws-sdk/client-lambda": "^3.310.0", "@aws-sdk/util-utf8-node": "^3.259.0" } -} +} \ No newline at end of file diff --git a/packages/commons/src/Utility.ts b/packages/commons/src/Utility.ts index c1c2a0fc05..2bb5eff446 100644 --- a/packages/commons/src/Utility.ts +++ b/packages/commons/src/Utility.ts @@ -1,51 +1,51 @@ /** * ## Intro * Utility is a base class that other Powertools utilites can extend to inherit shared logic. - * - * + * + * * ## Key features - * * Cold Start heuristic to determine if the current - * + * * Cold Start heuristic to determine if the current + * * ## Usage - * + * * ### Cold Start - * + * * Cold start is a term commonly used to describe the `Init` phase of a Lambda function. In this phase, Lambda creates or unfreezes an execution environment with the configured resources, downloads the code for the function and all layers, initializes any extensions, initializes the runtime, and then runs the function’s initialization code (the code outside the main handler). The Init phase happens either during the first invocation, or in advance of function invocations if you have enabled provisioned concurrency. - * + * * To learn more about the Lambda execution environment lifecycle, see the [Execution environment section](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-context.html) of the AWS Lambda documentation. - * + * * As a Powertools user you probably won't be using this class directly, in fact if you use other Powertools utilities the cold start heuristic found here is already used to: * * Add a `coldStart` key to the structured logs when injecting context information in `Logger` * * Emit a metric during a cold start function invocation in `Metrics` * * Annotate the invocation segment with a `coldStart` key in `Tracer` - * + * * If you want to use this logic in your own utilities, `Utility` provides two methods: - * + * * #### `getColdStart()` - * + * * Since the `Utility` class is instantiated outside of the Lambda handler it will persist across invocations of the same execution environment. This means that if you call `getColdStart()` multiple times, it will return `true` during the first invocation, and `false` afterwards. - * + * * @example * ```typescript * import { Utility } from '@aws-lambda-powertools/commons'; - * + * * const utility = new Utility(); - * + * * export const handler = async (_event: any, _context: any) => { * utility.getColdStart(); * }; * ``` - * + * * #### `isColdStart()` - * + * * This method is an alias of `getColdStart()` and is exposed for convenience and better readability in certain usages. - * + * * @example * ```typescript * import { Utility } from '@aws-lambda-powertools/commons'; - * + * * const utility = new Utility(); - * + * * export const handler = async (_event: any, _context: any) => { * if (utility.isColdStart()) { * // do something, this block is only executed on the first invocation of the function @@ -56,7 +56,7 @@ * ``` */ export class Utility { - private coldStart: boolean = true; + private coldStart = true; private readonly defaultServiceName: string = 'service_undefined'; public getColdStart(): boolean { @@ -78,12 +78,12 @@ export class Utility { } /** - * Validate that the service name provided is valid. - * Used internally during initialization. - * - * @param serviceName - Service name to validate - */ + * Validate that the service name provided is valid. + * Used internally during initialization. + * + * @param serviceName - Service name to validate + */ protected isValidServiceName(serviceName?: string): boolean { return typeof serviceName === 'string' && serviceName.trim().length > 0; } -} \ No newline at end of file +} diff --git a/packages/commons/src/config/ConfigService.ts b/packages/commons/src/config/ConfigService.ts index 22b8fae232..e149c27229 100644 --- a/packages/commons/src/config/ConfigService.ts +++ b/packages/commons/src/config/ConfigService.ts @@ -8,7 +8,6 @@ * @abstract */ abstract class ConfigService { - /** * It returns the value of an environment variable that has given name. * @@ -26,7 +25,7 @@ abstract class ConfigService { /** * It returns the value of the _X_AMZN_TRACE_ID environment variable. - * + * * The AWS X-Ray Trace data available in the environment variable has this format: * `Root=1-5759e988-bd862e3fe1be46a994272793;Parent=557abcec3ee5a047;Sampled=1`, * @@ -45,6 +44,4 @@ abstract class ConfigService { public abstract isValueTrue(value: string): boolean; } -export { - ConfigService, -}; \ No newline at end of file +export { ConfigService }; diff --git a/packages/commons/src/config/EnvironmentVariablesService.ts b/packages/commons/src/config/EnvironmentVariablesService.ts index 63bc9f6561..1220b6d175 100644 --- a/packages/commons/src/config/EnvironmentVariablesService.ts +++ b/packages/commons/src/config/EnvironmentVariablesService.ts @@ -14,7 +14,6 @@ import { ConfigService } from '.'; * @see https://awslabs.github.io/aws-lambda-powertools-typescript/latest/#environment-variables */ class EnvironmentVariablesService extends ConfigService { - /** * @see https://awslabs.github.io/aws-lambda-powertools-typescript/latest/#environment-variables * @protected @@ -44,7 +43,7 @@ class EnvironmentVariablesService extends ConfigService { /** * It returns the value of the _X_AMZN_TRACE_ID environment variable. - * + * * The AWS X-Ray Trace data available in the environment variable has this format: * `Root=1-5759e988-bd862e3fe1be46a994272793;Parent=557abcec3ee5a047;Sampled=1`, * @@ -60,7 +59,7 @@ class EnvironmentVariablesService extends ConfigService { /** * It returns true if the Sampled flag is set in the _X_AMZN_TRACE_ID environment variable. - * + * * The AWS X-Ray Trace data available in the environment variable has this format: * `Root=1-5759e988-bd862e3fe1be46a994272793;Parent=557abcec3ee5a047;Sampled=1`, * @@ -79,7 +78,7 @@ class EnvironmentVariablesService extends ConfigService { * @returns boolean */ public isValueTrue(value: string): boolean { - const truthyValues: string[] = [ '1', 'y', 'yes', 't', 'true', 'on' ]; + const truthyValues: string[] = ['1', 'y', 'yes', 't', 'true', 'on']; return truthyValues.includes(value.toLowerCase()); } @@ -98,8 +97,7 @@ class EnvironmentVariablesService extends ConfigService { const xRayTraceData: Record = {}; xRayTraceEnv.split(';').forEach((field) => { - - const [ key, value ] = field.split('='); + const [key, value] = field.split('='); xRayTraceData[key] = value; }); @@ -108,6 +106,4 @@ class EnvironmentVariablesService extends ConfigService { } } -export { - EnvironmentVariablesService, -}; \ No newline at end of file +export { EnvironmentVariablesService }; diff --git a/packages/commons/src/config/index.ts b/packages/commons/src/config/index.ts index 86697bdab3..1530e49ebf 100644 --- a/packages/commons/src/config/index.ts +++ b/packages/commons/src/config/index.ts @@ -1,2 +1,2 @@ export * from './ConfigService'; -export * from './EnvironmentVariablesService'; \ No newline at end of file +export * from './EnvironmentVariablesService'; diff --git a/packages/commons/src/index.ts b/packages/commons/src/index.ts index ee40dcac9b..ec26c92bc4 100644 --- a/packages/commons/src/index.ts +++ b/packages/commons/src/index.ts @@ -3,4 +3,4 @@ export * from './Utility'; export * from './config'; export * as ContextExamples from './samples/resources/contexts'; export * as Events from './samples/resources/events'; -export * from './types/middy'; \ No newline at end of file +export * from './types/middy'; diff --git a/packages/commons/src/samples/resources/contexts/hello-world.ts b/packages/commons/src/samples/resources/contexts/hello-world.ts index 16f707376e..0f26aed05f 100644 --- a/packages/commons/src/samples/resources/contexts/hello-world.ts +++ b/packages/commons/src/samples/resources/contexts/hello-world.ts @@ -7,7 +7,8 @@ const helloworldContext: Context = { memoryLimitInMB: '128', logGroupName: '/aws/lambda/foo-bar-function-123456abcdef', logStreamName: '2021/03/09/[$LATEST]abcdef123456abcdef123456abcdef123456', - invokedFunctionArn: 'arn:aws:lambda:eu-west-1:123456789012:function:foo-bar-function', + invokedFunctionArn: + 'arn:aws:lambda:eu-west-1:123456789012:function:foo-bar-function', awsRequestId: 'c6af9ac6-7b61-11e6-9a41-93e812345678', getRemainingTimeInMillis: () => 1234, done: () => console.log('Done!'), @@ -15,6 +16,4 @@ const helloworldContext: Context = { succeed: () => console.log('Succeeded!'), }; -export { - helloworldContext, -}; \ No newline at end of file +export { helloworldContext }; diff --git a/packages/commons/src/samples/resources/contexts/index.ts b/packages/commons/src/samples/resources/contexts/index.ts index a4f7fe564e..436927eee2 100644 --- a/packages/commons/src/samples/resources/contexts/index.ts +++ b/packages/commons/src/samples/resources/contexts/index.ts @@ -1 +1 @@ -export * from './hello-world'; \ No newline at end of file +export * from './hello-world'; diff --git a/packages/commons/src/samples/resources/events/index.ts b/packages/commons/src/samples/resources/events/index.ts index 0c3a1f2c8a..45582e4428 100644 --- a/packages/commons/src/samples/resources/events/index.ts +++ b/packages/commons/src/samples/resources/events/index.ts @@ -1 +1 @@ -export * as Custom from './custom'; \ No newline at end of file +export * as Custom from './custom'; diff --git a/packages/commons/src/types/middy.ts b/packages/commons/src/types/middy.ts index 703e0915f2..fe060ece0a 100644 --- a/packages/commons/src/types/middy.ts +++ b/packages/commons/src/types/middy.ts @@ -7,27 +7,42 @@ import { Context } from 'aws-lambda'; * and use `tsc` to compile their code will get an error if we import from @middy/core, see #1068. * Given that we use a subset of the @middy/core types, we can define them here and avoid the dependency. */ -type Request = { - event: TEvent - context: TContext - response: TResult | null - error: TErr | null +type Request< + TEvent = unknown, + TResult = unknown, + TErr = Error, + TContext extends Context = Context +> = { + event: TEvent; + context: TContext; + response: TResult | null; + error: TErr | null; internal: { - [key: string]: unknown - } + [key: string]: unknown; + }; }; -declare type MiddlewareFn = (request: Request) => unknown; +declare type MiddlewareFn< + TEvent = unknown, + TResult = unknown, + TErr = Error, + TContext extends Context = Context +> = (request: Request) => unknown; -export type MiddlewareLikeObj = { - before?: MiddlewareFn - after?: MiddlewareFn - onError?: MiddlewareFn +export type MiddlewareLikeObj< + TEvent = unknown, + TResult = unknown, + TErr = Error, + TContext extends Context = Context +> = { + before?: MiddlewareFn; + after?: MiddlewareFn; + onError?: MiddlewareFn; }; export type MiddyLikeRequest = { - event: unknown - context: Context - response: unknown | null - error: Error | null -}; \ No newline at end of file + event: unknown; + context: Context; + response: unknown | null; + error: Error | null; +}; diff --git a/packages/commons/src/utils/lambda/LambdaInterface.ts b/packages/commons/src/utils/lambda/LambdaInterface.ts index 957ab1749c..fc2e95ffb7 100644 --- a/packages/commons/src/utils/lambda/LambdaInterface.ts +++ b/packages/commons/src/utils/lambda/LambdaInterface.ts @@ -3,18 +3,16 @@ import { Handler } from 'aws-lambda'; export type SyncHandler = ( event: Parameters[0], context: Parameters[1], - callback: Parameters[2], + callback: Parameters[2] ) => void; export type AsyncHandler = ( event: Parameters[0], - context: Parameters[1], + context: Parameters[1] ) => Promise[2]>[1]>>; interface LambdaInterface { - handler: SyncHandler | AsyncHandler + handler: SyncHandler | AsyncHandler; } -export { - LambdaInterface -}; \ No newline at end of file +export { LambdaInterface }; diff --git a/packages/commons/src/utils/lambda/index.ts b/packages/commons/src/utils/lambda/index.ts index 059e4dbaf7..f57f966d0c 100644 --- a/packages/commons/src/utils/lambda/index.ts +++ b/packages/commons/src/utils/lambda/index.ts @@ -1 +1 @@ -export * from './LambdaInterface'; \ No newline at end of file +export * from './LambdaInterface'; diff --git a/packages/commons/tests/unit/InvocationLogs.test.ts b/packages/commons/tests/unit/InvocationLogs.test.ts index e0617c8ebe..5c457a6dbe 100644 --- a/packages/commons/tests/unit/InvocationLogs.test.ts +++ b/packages/commons/tests/unit/InvocationLogs.test.ts @@ -1,8 +1,8 @@ /** * Test InvocationLogs class - * + * * @group unit/commons/invocationLogs - * + * */ import { InvocationLogs, LEVEL } from '../utils/InvocationLogs'; @@ -17,28 +17,34 @@ REPORT RequestId: c6af9ac6-7b61-11e6-9a41-93e812345678\tDuration: 2.16 ms\tBille describe('Constructor', () => { test('it should parse base64 text correctly', () => { - const invocationLogs = new InvocationLogs(Buffer.from(exampleLogs).toString('base64')); + const invocationLogs = new InvocationLogs( + Buffer.from(exampleLogs).toString('base64') + ); expect(invocationLogs.getFunctionLogs(LEVEL.DEBUG).length).toBe(1); expect(invocationLogs.getFunctionLogs(LEVEL.INFO).length).toBe(2); expect(invocationLogs.getFunctionLogs(LEVEL.ERROR).length).toBe(1); }); - }); describe('doesAnyFunctionLogsContains()', () => { let invocationLogs: InvocationLogs; beforeEach(() => { - invocationLogs = new InvocationLogs(Buffer.from(exampleLogs).toString('base64')); + invocationLogs = new InvocationLogs( + Buffer.from(exampleLogs).toString('base64') + ); }); test('it should return true if the text appear in any logs', () => { const phraseInMessage = 'This is'; - expect(invocationLogs.doesAnyFunctionLogsContains(phraseInMessage)).toBe(true); - + expect(invocationLogs.doesAnyFunctionLogsContains(phraseInMessage)).toBe( + true + ); }); test('it should return false if the text does not appear in any logs', () => { const phraseNotInMessage = 'A quick brown fox jumps over the lazy dog'; - expect(invocationLogs.doesAnyFunctionLogsContains(phraseNotInMessage)).toBe(false); + expect(invocationLogs.doesAnyFunctionLogsContains(phraseNotInMessage)).toBe( + false + ); }); test('it should return true for key in the log', () => { @@ -54,21 +60,35 @@ describe('doesAnyFunctionLogsContains()', () => { const textInStartLine = 'Version: $LATEST'; const textInEndLine = 'END RequestId'; const textInReportLine = 'Billed Duration'; - expect(invocationLogs.doesAnyFunctionLogsContains(textInStartLine)).toBe(false); - expect(invocationLogs.doesAnyFunctionLogsContains(textInEndLine)).toBe(false); - expect(invocationLogs.doesAnyFunctionLogsContains(textInReportLine)).toBe(false); + expect(invocationLogs.doesAnyFunctionLogsContains(textInStartLine)).toBe( + false + ); + expect(invocationLogs.doesAnyFunctionLogsContains(textInEndLine)).toBe( + false + ); + expect(invocationLogs.doesAnyFunctionLogsContains(textInReportLine)).toBe( + false + ); }); test('it should apply filter log based on the given level', () => { - const debugLogHasWordINFO = invocationLogs.doesAnyFunctionLogsContains('INFO', LEVEL.DEBUG); + const debugLogHasWordINFO = invocationLogs.doesAnyFunctionLogsContains( + 'INFO', + LEVEL.DEBUG + ); expect(debugLogHasWordINFO).toBe(true); - const infoLogHasWordINFO = invocationLogs.doesAnyFunctionLogsContains('INFO', LEVEL.INFO); + const infoLogHasWordINFO = invocationLogs.doesAnyFunctionLogsContains( + 'INFO', + LEVEL.INFO + ); expect(infoLogHasWordINFO).toBe(true); - const errorLogHasWordINFO = invocationLogs.doesAnyFunctionLogsContains('INFO', LEVEL.ERROR); + const errorLogHasWordINFO = invocationLogs.doesAnyFunctionLogsContains( + 'INFO', + LEVEL.ERROR + ); expect(errorLogHasWordINFO).toBe(false); - }); }); @@ -76,7 +96,9 @@ describe('getFunctionLogs()', () => { let invocationLogs: InvocationLogs; beforeEach(() => { - invocationLogs = new InvocationLogs(Buffer.from(exampleLogs).toString('base64')); + invocationLogs = new InvocationLogs( + Buffer.from(exampleLogs).toString('base64') + ); }); test('it should retrive logs of the given level only', () => { @@ -104,17 +126,21 @@ describe('getFunctionLogs()', () => { describe('parseFunctionLog()', () => { test('it should return object with the correct values based on the given log', () => { - const rawLogStr = '{"cold_start":true,"function_arn":"arn:aws:lambda:eu-west-1:561912387782:function:loggerMiddyStandardFeatures-c555a2ec-1121-4586-9c04-185ab36ea34c","function_memory_size":128,"function_name":"loggerMiddyStandardFeatures-c555a2ec-1121-4586-9c04-185ab36ea34c","function_request_id":"7f586697-238a-4c3b-9250-a5f057c1119c","level":"DEBUG","message":"This is a DEBUG log but contains the word INFO some context and persistent key","service":"logger-e2e-testing","timestamp":"2022-01-27T16:04:39.323Z","persistentKey":"works"}'; + const rawLogStr = + '{"cold_start":true,"function_arn":"arn:aws:lambda:eu-west-1:561912387782:function:loggerMiddyStandardFeatures-c555a2ec-1121-4586-9c04-185ab36ea34c","function_memory_size":128,"function_name":"loggerMiddyStandardFeatures-c555a2ec-1121-4586-9c04-185ab36ea34c","function_request_id":"7f586697-238a-4c3b-9250-a5f057c1119c","level":"DEBUG","message":"This is a DEBUG log but contains the word INFO some context and persistent key","service":"logger-e2e-testing","timestamp":"2022-01-27T16:04:39.323Z","persistentKey":"works"}'; const logObj = InvocationLogs.parseFunctionLog(rawLogStr); expect(logObj).toStrictEqual({ cold_start: true, - function_arn: 'arn:aws:lambda:eu-west-1:561912387782:function:loggerMiddyStandardFeatures-c555a2ec-1121-4586-9c04-185ab36ea34c', + function_arn: + 'arn:aws:lambda:eu-west-1:561912387782:function:loggerMiddyStandardFeatures-c555a2ec-1121-4586-9c04-185ab36ea34c', function_memory_size: 128, - function_name: 'loggerMiddyStandardFeatures-c555a2ec-1121-4586-9c04-185ab36ea34c', + function_name: + 'loggerMiddyStandardFeatures-c555a2ec-1121-4586-9c04-185ab36ea34c', function_request_id: '7f586697-238a-4c3b-9250-a5f057c1119c', level: 'DEBUG', - message: 'This is a DEBUG log but contains the word INFO some context and persistent key', + message: + 'This is a DEBUG log but contains the word INFO some context and persistent key', service: 'logger-e2e-testing', timestamp: '2022-01-27T16:04:39.323Z', persistentKey: 'works', @@ -127,4 +153,4 @@ describe('parseFunctionLog()', () => { InvocationLogs.parseFunctionLog(notJSONstring); }).toThrow(Error); }); -}); \ No newline at end of file +}); diff --git a/packages/commons/tests/unit/LambdaInterface.test.ts b/packages/commons/tests/unit/LambdaInterface.test.ts index 4c0c8902af..7fb7f0e389 100644 --- a/packages/commons/tests/unit/LambdaInterface.test.ts +++ b/packages/commons/tests/unit/LambdaInterface.test.ts @@ -5,13 +5,21 @@ */ import { Handler } from 'aws-lambda'; import { Callback, Context } from 'aws-lambda'; -import { ContextExamples, SyncHandler, AsyncHandler, LambdaInterface } from '../../src'; +import { + ContextExamples, + SyncHandler, + AsyncHandler, + LambdaInterface, +} from '../../src'; describe('LambdaInterface with arrow function', () => { test('it compiles when given a callback', async () => { class LambdaFunction implements LambdaInterface { - - public handler: SyncHandler = async (_event: unknown, context: Context, _callback: Callback) => { + public handler: SyncHandler = async ( + _event: unknown, + context: Context, + _callback: Callback + ) => { context.done(); context.fail(new Error('test Error')); context.succeed('test succeed'); @@ -20,13 +28,19 @@ describe('LambdaInterface with arrow function', () => { }; } - await new LambdaFunction().handler({}, ContextExamples.helloworldContext, () => console.log('Lambda invoked!')); + await new LambdaFunction().handler( + {}, + ContextExamples.helloworldContext, + () => console.log('Lambda invoked!') + ); }); test('it compiles when not given a callback', async () => { class LambdaFunction implements LambdaInterface { - - public handler: AsyncHandler = async (_event: unknown, context: Context) => { + public handler: AsyncHandler = async ( + _event: unknown, + context: Context + ) => { context.getRemainingTimeInMillis(); }; } @@ -38,22 +52,28 @@ describe('LambdaInterface with arrow function', () => { describe('LambdaInterface with standard function', () => { test('it compiles when given a callback', async () => { class LambdaFunction implements LambdaInterface { - - public handler(_event: unknown, context: Context, _callback: Callback): void { + public handler( + _event: unknown, + context: Context, + _callback: Callback + ): void { context.getRemainingTimeInMillis(); _callback(null, 'Hello World'); } } - await new LambdaFunction().handler({}, ContextExamples.helloworldContext, () => console.log('Lambda invoked!')); + await new LambdaFunction().handler( + {}, + ContextExamples.helloworldContext, + () => console.log('Lambda invoked!') + ); }); test('it compiles when not given a callback', async () => { class LambdaFunction implements LambdaInterface { - - public async handler (_event: unknown, context: Context): Promise { + public async handler(_event: unknown, context: Context): Promise { context.getRemainingTimeInMillis(); - + return new Promise((resolve) => { resolve('test promise'); }); @@ -62,38 +82,41 @@ describe('LambdaInterface with standard function', () => { await new LambdaFunction().handler({}, ContextExamples.helloworldContext); }); - }); describe('LambdaInterface with decorator', () => { type HandlerMethodDecorator = ( target: LambdaInterface, propertyKey: string | symbol, - descriptor: TypedPropertyDescriptor> | TypedPropertyDescriptor> + descriptor: + | TypedPropertyDescriptor> + | TypedPropertyDescriptor> ) => void; class DummyModule { - public dummyDecorator(): HandlerMethodDecorator { return (target, _propertyKey, descriptor) => { const originalMethod = descriptor.value; - - descriptor.value = ( async (event, context, callback) => { - + + descriptor.value = async (event, context, callback) => { let result: unknown; try { console.log(`Invoking ${String(_propertyKey)}`); - result = await originalMethod?.apply(this, [ event, context, callback ]); + result = await originalMethod?.apply(this, [ + event, + context, + callback, + ]); console.log(`Invoked ${String(_propertyKey)}`); } catch (error) { throw error; } finally { console.log(`Finally from decorator`); } - + return result; - }); - + }; + return descriptor; }; } @@ -102,36 +125,43 @@ describe('LambdaInterface with decorator', () => { const dummyModule = new DummyModule(); test('decorator without callback compile', async () => { - // WHEN class LambdaFunction implements LambdaInterface { - @dummyModule.dummyDecorator() // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - public async handler(_event: unknown, context: Context): Promise { + public async handler( + _event: unknown, + context: Context + ): Promise { context.getRemainingTimeInMillis(); - + return 'test'; } } - await new LambdaFunction().handler({}, ContextExamples.helloworldContext); + await new LambdaFunction().handler({}, ContextExamples.helloworldContext); }); test('decorator with callback compile', async () => { - // WHEN class LambdaFunction implements LambdaInterface { - @dummyModule.dummyDecorator() // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - public handler(_event: unknown, context: Context, _callback: Callback): void { + public handler( + _event: unknown, + context: Context, + _callback: Callback + ): void { context.getRemainingTimeInMillis(); } } - await new LambdaFunction().handler({}, ContextExamples.helloworldContext, () => console.log('Lambda invoked!')); + await new LambdaFunction().handler( + {}, + ContextExamples.helloworldContext, + () => console.log('Lambda invoked!') + ); }); }); diff --git a/packages/commons/tests/unit/Utility.test.ts b/packages/commons/tests/unit/Utility.test.ts index b8a89f4af7..f289d15d85 100644 --- a/packages/commons/tests/unit/Utility.test.ts +++ b/packages/commons/tests/unit/Utility.test.ts @@ -6,19 +6,18 @@ import { Utility } from '../../src'; describe('Class: Utility', () => { - beforeEach(() => { jest.clearAllMocks(); jest.resetModules(); }); - describe('Method: getDefaultServiceName', ()=> { - test('it should return the default service name', ()=> { + describe('Method: getDefaultServiceName', () => { + test('it should return the default service name', () => { class PowerTool extends Utility { public constructor() { super(); } - + public dummyMethod(): string { return this.getDefaultServiceName(); } @@ -31,9 +30,7 @@ describe('Class: Utility', () => { }); describe('Method: getColdStart', () => { - test('when called multiple times on the parent class, it returns true the first time, then false afterwards', () => { - // Prepare const utility = new Utility(); const getColdStartSpy = jest.spyOn(utility, 'getColdStart'); @@ -44,7 +41,7 @@ describe('Class: Utility', () => { utility.getColdStart(); utility.getColdStart(); utility.getColdStart(); - + // Assess expect(getColdStartSpy).toHaveBeenCalledTimes(5); expect(getColdStartSpy.mock.results).toEqual([ @@ -54,11 +51,9 @@ describe('Class: Utility', () => { expect.objectContaining({ value: false }), expect.objectContaining({ value: false }), ]); - }); test('when called multiple times on a child class, it returns true the first time, then false afterwards', () => { - // Prepare class PowerTool extends Utility { public constructor() { @@ -90,15 +85,11 @@ describe('Class: Utility', () => { expect.objectContaining({ value: false }), expect.objectContaining({ value: false }), ]); - }); - }); describe('Method: isColdStart', () => { - test('when called multiple times on the parent class, it returns true the first time, then false afterwards', () => { - // Prepare const utility = new Utility(); const isColdStartSpy = jest.spyOn(utility, 'isColdStart'); @@ -119,11 +110,9 @@ describe('Class: Utility', () => { expect.objectContaining({ value: false }), expect.objectContaining({ value: false }), ]); - }); test('when called multiple times on a child class, it returns true the first time, then false afterwards', () => { - // Prepare class PowerTool extends Utility { public constructor() { @@ -155,9 +144,7 @@ describe('Class: Utility', () => { expect.objectContaining({ value: false }), expect.objectContaining({ value: false }), ]); - }); - }); describe('Method: isValidServiceName', () => { @@ -166,25 +153,25 @@ describe('Class: Utility', () => { super(); } - public dummyMethod(name:string): boolean { + public dummyMethod(name: string): boolean { return this.isValidServiceName(name); } } - test('it should allow valid strings', ()=> { + test('it should allow valid strings', () => { const powertool = new PowerTool(); const goodName = 'serverlessAirline'; const result = powertool.dummyMethod(goodName); - - expect(result).toBe(true); + + expect(result).toBe(true); }); - test('it should not allow empty strings', ()=> { + test('it should not allow empty strings', () => { const tooShort = ''; const powertool = new PowerTool(); const result = powertool.dummyMethod(tooShort); - + expect(result).toBe(false); }); }); -}); \ No newline at end of file +}); diff --git a/packages/commons/tests/unit/config/EnvironmentVariablesService.test.ts b/packages/commons/tests/unit/config/EnvironmentVariablesService.test.ts index 06e458282b..055a57ac61 100644 --- a/packages/commons/tests/unit/config/EnvironmentVariablesService.test.ts +++ b/packages/commons/tests/unit/config/EnvironmentVariablesService.test.ts @@ -7,7 +7,6 @@ import { EnvironmentVariablesService } from '../../../src/config'; describe('Class: EnvironmentVariablesService', () => { - const ENVIRONMENT_VARIABLES = process.env; beforeEach(() => { @@ -20,9 +19,7 @@ describe('Class: EnvironmentVariablesService', () => { }); describe('Method: get', () => { - test('When the variable IS present, it returns the value of a runtime variable', () => { - // Prepare process.env.CUSTOM_VARIABLE = 'my custom value'; const service = new EnvironmentVariablesService(); @@ -32,11 +29,9 @@ describe('Class: EnvironmentVariablesService', () => { // Assess expect(value).toEqual('my custom value'); - }); test('When the variable IS NOT present, it returns an empty string', () => { - // Prepare delete process.env.CUSTOM_VARIABLE; const service = new EnvironmentVariablesService(); @@ -46,15 +41,11 @@ describe('Class: EnvironmentVariablesService', () => { // Assess expect(value).toEqual(''); - }); - }); describe('Method: getServiceName', () => { - test('It returns the value of the environment variable POWERTOOLS_SERVICE_NAME', () => { - // Prepare process.env.POWERTOOLS_SERVICE_NAME = 'shopping-cart-api'; const service = new EnvironmentVariablesService(); @@ -65,13 +56,10 @@ describe('Class: EnvironmentVariablesService', () => { // Assess expect(value).toEqual('shopping-cart-api'); }); - }); describe('Method: getXrayTraceId', () => { - test('It returns the value of the environment variable _X_AMZN_TRACE_ID', () => { - // Prepare process.env._X_AMZN_TRACE_ID = 'abcd123456789'; const service = new EnvironmentVariablesService(); @@ -83,9 +71,9 @@ describe('Class: EnvironmentVariablesService', () => { expect(value).toEqual('abcd123456789'); }); test('It returns the value of the Root X-Ray segment ID properly formatted', () => { - // Prepare - process.env._X_AMZN_TRACE_ID = 'Root=1-5759e988-bd862e3fe1be46a994272793;Parent=557abcec3ee5a047;Sampled=1'; + process.env._X_AMZN_TRACE_ID = + 'Root=1-5759e988-bd862e3fe1be46a994272793;Parent=557abcec3ee5a047;Sampled=1'; const service = new EnvironmentVariablesService(); // Act @@ -96,7 +84,6 @@ describe('Class: EnvironmentVariablesService', () => { }); test('It returns the value of the Root X-Ray segment ID properly formatted', () => { - // Prepare delete process.env._X_AMZN_TRACE_ID; const service = new EnvironmentVariablesService(); @@ -107,15 +94,13 @@ describe('Class: EnvironmentVariablesService', () => { // Assess expect(value).toEqual(undefined); }); - }); describe('Method: getXrayTraceSampled', () => { - test('It returns true if the Sampled flag is set in the _X_AMZN_TRACE_ID environment variable', () => { - // Prepare - process.env._X_AMZN_TRACE_ID = 'Root=1-5759e988-bd862e3fe1be46a994272793;Parent=557abcec3ee5a047;Sampled=1'; + process.env._X_AMZN_TRACE_ID = + 'Root=1-5759e988-bd862e3fe1be46a994272793;Parent=557abcec3ee5a047;Sampled=1'; const service = new EnvironmentVariablesService(); // Act @@ -126,9 +111,9 @@ describe('Class: EnvironmentVariablesService', () => { }); test('It returns false if the Sampled flag is not set in the _X_AMZN_TRACE_ID environment variable', () => { - // Prepare - process.env._X_AMZN_TRACE_ID = 'Root=1-5759e988-bd862e3fe1be46a994272793;Parent=557abcec3ee5a047'; + process.env._X_AMZN_TRACE_ID = + 'Root=1-5759e988-bd862e3fe1be46a994272793;Parent=557abcec3ee5a047'; const service = new EnvironmentVariablesService(); // Act @@ -139,7 +124,6 @@ describe('Class: EnvironmentVariablesService', () => { }); it('It returns false when no _X_AMZN_TRACE_ID environment variable is present', () => { - // Prepare delete process.env._X_AMZN_TRACE_ID; const service = new EnvironmentVariablesService(); @@ -153,30 +137,30 @@ describe('Class: EnvironmentVariablesService', () => { }); describe('Method: isValueTrue', () => { - const valuesToTest: Array> = [ - [ '1', true ], - [ 'y', true ], - [ 'yes', true ], - [ 't', true ], - [ 'TRUE', true ], - [ 'on', true ], - [ '', false ], - [ 'false', false ], - [ 'fasle', false ], - [ 'somethingsilly', false ], - [ '0', false ] + ['1', true], + ['y', true], + ['yes', true], + ['t', true], + ['TRUE', true], + ['on', true], + ['', false], + ['false', false], + ['fasle', false], + ['somethingsilly', false], + ['0', false], ]; - test.each(valuesToTest)('it takes string "%s" and returns %s', (input, output) => { - // Prepare - const service = new EnvironmentVariablesService(); - // Act - const value = service.isValueTrue(input as string); - // Assess - expect(value).toBe(output); - }); - + test.each(valuesToTest)( + 'it takes string "%s" and returns %s', + (input, output) => { + // Prepare + const service = new EnvironmentVariablesService(); + // Act + const value = service.isValueTrue(input as string); + // Assess + expect(value).toBe(output); + } + ); }); - -}); \ No newline at end of file +}); diff --git a/packages/commons/tests/utils/InvocationLogs.ts b/packages/commons/tests/utils/InvocationLogs.ts index fd2b9f3e4c..00d3cc03ce 100644 --- a/packages/commons/tests/utils/InvocationLogs.ts +++ b/packages/commons/tests/utils/InvocationLogs.ts @@ -9,14 +9,14 @@ export enum LEVEL { } export type ErrorField = { - name: string - message: string - stack: string + name: string; + message: string; + stack: string; }; export type FunctionLog = { - level: LEVEL - error: ErrorField + level: LEVEL; + error: ErrorField; } & { [key: string]: unknown }; export class InvocationLogs { @@ -49,8 +49,13 @@ export class InvocationLogs { * @param levelToFilter level to filter * @returns */ - public doesAnyFunctionLogsContains(text: string, levelToFilter?: LEVEL): boolean { - const filteredLogs = this.getFunctionLogs(levelToFilter).filter((log) => log.includes(text)); + public doesAnyFunctionLogsContains( + text: string, + levelToFilter?: LEVEL + ): boolean { + const filteredLogs = this.getFunctionLogs(levelToFilter).filter((log) => + log.includes(text) + ); return filteredLogs.length > 0; } @@ -65,7 +70,7 @@ export class InvocationLogs { /** * Return the index of the log that contains END RequestId - * @param logs + * @param logs * @returns {number} index of the log that contains END RequestId */ public static getEndLogIndex(logs: string[]): number { @@ -75,7 +80,7 @@ export class InvocationLogs { /** * Return only logs from function, exclude START, END, REPORT, * and X-Ray log generated by the Lambda service. - * + * * @param {LEVEL} [levelToFilter] - Level to filter the logs * @returns Array of function logs, filtered by level if provided */ @@ -88,7 +93,7 @@ export class InvocationLogs { filteredLogs = filteredLogs.filter((log) => { try { const parsedLog = InvocationLogs.parseFunctionLog(log); - + return parsedLog.level == levelToFilter; } catch (error) { // If log is not from structured logging : such as metrics one. diff --git a/packages/commons/tests/utils/cdk-cli.ts b/packages/commons/tests/utils/cdk-cli.ts index ed6ff21fd3..65c78fa14c 100644 --- a/packages/commons/tests/utils/cdk-cli.ts +++ b/packages/commons/tests/utils/cdk-cli.ts @@ -4,7 +4,11 @@ import { CloudFormationDeployments } from 'aws-cdk/lib/api/cloudformation-deploy import { SdkProvider } from 'aws-cdk/lib/api/aws-auth'; import { DeployStackResult } from 'aws-cdk/lib/api/deploy-stack'; -export const deployStack = async (app: App, stack: Stack, quiet?: boolean): Promise => { +export const deployStack = async ( + app: App, + stack: Stack, + quiet?: boolean +): Promise => { const stackArtifact = getStackArtifact(app, stack); const cloudFormation = await createCloudFormationDeployments(); @@ -15,7 +19,11 @@ export const deployStack = async (app: App, stack: Stack, quiet?: boolean): Prom }); }; -export const destroyStack = async (app: App, stack: Stack, quiet?: boolean): Promise => { +export const destroyStack = async ( + app: App, + stack: Stack, + quiet?: boolean +): Promise => { const stackArtifact = getStackArtifact(app, stack); const cloudFormation = await createCloudFormationDeployments(); @@ -26,7 +34,10 @@ export const destroyStack = async (app: App, stack: Stack, quiet?: boolean): Pro }); }; -const getStackArtifact = (app: App, stack: Stack): cxapi.CloudFormationStackArtifact => { +const getStackArtifact = ( + app: App, + stack: Stack +): cxapi.CloudFormationStackArtifact => { const synthesized = app.synth(); // Reload the synthesized artifact for stack using the cxapi from dependencies @@ -39,11 +50,12 @@ const getStackArtifact = (app: App, stack: Stack): cxapi.CloudFormationStackArti ) as cxapi.CloudFormationStackArtifact; }; -const createCloudFormationDeployments = async (): Promise => { - const sdkProvider = await SdkProvider.withAwsCliCompatibleDefaults({ - profile: process.env.AWS_PROFILE, - }); - const cloudFormation = new CloudFormationDeployments({ sdkProvider }); +const createCloudFormationDeployments = + async (): Promise => { + const sdkProvider = await SdkProvider.withAwsCliCompatibleDefaults({ + profile: process.env.AWS_PROFILE, + }); + const cloudFormation = new CloudFormationDeployments({ sdkProvider }); - return cloudFormation; -}; + return cloudFormation; + }; diff --git a/packages/commons/tests/utils/e2eUtils.ts b/packages/commons/tests/utils/e2eUtils.ts index 1469ea0ab1..6531c78db7 100644 --- a/packages/commons/tests/utils/e2eUtils.ts +++ b/packages/commons/tests/utils/e2eUtils.ts @@ -1,11 +1,11 @@ -/** +/** * E2E utils is used by e2e tests. They are helper function that calls either CDK or SDK - * to interact with services. -*/ + * to interact with services. + */ import { App, CfnOutput, Stack, Duration } from 'aws-cdk-lib'; import { NodejsFunction, - NodejsFunctionProps + NodejsFunctionProps, } from 'aws-cdk-lib/aws-lambda-nodejs'; import { Runtime, Tracing } from 'aws-cdk-lib/aws-lambda'; import { RetentionDays } from 'aws-cdk-lib/aws-logs'; @@ -16,8 +16,8 @@ import { InvocationLogs } from './InvocationLogs'; const lambdaClient = new LambdaClient({}); -const testRuntimeKeys = [ 'nodejs14x', 'nodejs16x', 'nodejs18x' ]; -export type TestRuntimesKey = typeof testRuntimeKeys[number]; +const testRuntimeKeys = ['nodejs14x', 'nodejs16x', 'nodejs18x']; +export type TestRuntimesKey = (typeof testRuntimeKeys)[number]; export const TEST_RUNTIMES: Record = { nodejs14x: Runtime.NODEJS_14_X, nodejs16x: Runtime.NODEJS_16_X, @@ -25,25 +25,28 @@ export const TEST_RUNTIMES: Record = { }; export type StackWithLambdaFunctionOptions = { - app: App - stackName: string - functionName: string - functionEntry: string - tracing?: Tracing - environment: {[key: string]: string} - logGroupOutputKey?: string - runtime: string - bundling?: NodejsFunctionProps['bundling'] - layers?: NodejsFunctionProps['layers'] - timeout?: Duration + app: App; + stackName: string; + functionName: string; + functionEntry: string; + tracing?: Tracing; + environment: { [key: string]: string }; + logGroupOutputKey?: string; + runtime: string; + bundling?: NodejsFunctionProps['bundling']; + layers?: NodejsFunctionProps['layers']; + timeout?: Duration; }; -type FunctionPayload = {[key: string]: string | boolean | number}; +type FunctionPayload = { [key: string]: string | boolean | number }; -export const isValidRuntimeKey = (runtime: string): runtime is TestRuntimesKey => testRuntimeKeys.includes(runtime); +export const isValidRuntimeKey = ( + runtime: string +): runtime is TestRuntimesKey => testRuntimeKeys.includes(runtime); -export const createStackWithLambdaFunction = (params: StackWithLambdaFunctionOptions): Stack => { - +export const createStackWithLambdaFunction = ( + params: StackWithLambdaFunctionOptions +): Stack => { const stack = new Stack(params.app, params.stackName); const testFunction = new NodejsFunction(stack, `testFunction`, { functionName: params.functionName, @@ -54,7 +57,7 @@ export const createStackWithLambdaFunction = (params: StackWithLambdaFunctionOpt bundling: params.bundling, layers: params.layers, logRetention: RetentionDays.ONE_DAY, - timeout: params.timeout + timeout: params.timeout, }); if (params.logGroupOutputKey) { @@ -62,48 +65,70 @@ export const createStackWithLambdaFunction = (params: StackWithLambdaFunctionOpt value: testFunction.logGroup.logGroupName, }); } - + return stack; }; -export const generateUniqueName = (name_prefix: string, uuid: string, runtime: string, testName: string): string => - `${name_prefix}-${runtime}-${uuid.substring(0,5)}-${testName}`.substring(0, 64); +export const generateUniqueName = ( + name_prefix: string, + uuid: string, + runtime: string, + testName: string +): string => + `${name_prefix}-${runtime}-${uuid.substring(0, 5)}-${testName}`.substring( + 0, + 64 + ); -export const invokeFunction = async (functionName: string, times: number = 1, invocationMode: 'PARALLEL' | 'SEQUENTIAL' = 'PARALLEL', payload: FunctionPayload = {}): Promise => { +export const invokeFunction = async ( + functionName: string, + times = 1, + invocationMode: 'PARALLEL' | 'SEQUENTIAL' = 'PARALLEL', + payload: FunctionPayload = {} +): Promise => { const invocationLogs: InvocationLogs[] = []; const promiseFactory = (index?: number): Promise => { const invokePromise = lambdaClient - .send(new InvokeCommand({ - FunctionName: functionName, - InvocationType: 'RequestResponse', - LogType: 'Tail', // Wait until execution completes and return all logs - Payload: fromUtf8(JSON.stringify({ - invocation: index, - ...payload - })), - })) + .send( + new InvokeCommand({ + FunctionName: functionName, + InvocationType: 'RequestResponse', + LogType: 'Tail', // Wait until execution completes and return all logs + Payload: fromUtf8( + JSON.stringify({ + invocation: index, + ...payload, + }) + ), + }) + ) .then((response) => { if (response?.LogResult) { invocationLogs.push(new InvocationLogs(response?.LogResult)); } else { - throw new Error('No LogResult field returned in the response of Lambda invocation. This should not happen.'); + throw new Error( + 'No LogResult field returned in the response of Lambda invocation. This should not happen.' + ); } }); return invokePromise; }; - - const promiseFactories = Array.from({ length: times }, () => promiseFactory ); - const invocation = invocationMode == 'PARALLEL' - ? Promise.all(promiseFactories.map((factory, index) => factory(index))) - : chainPromises(promiseFactories); + + const promiseFactories = Array.from({ length: times }, () => promiseFactory); + const invocation = + invocationMode == 'PARALLEL' + ? Promise.all(promiseFactories.map((factory, index) => factory(index))) + : chainPromises(promiseFactories); await invocation; - return invocationLogs; + return invocationLogs; }; -const chainPromises = async (promiseFactories: ((index?: number) => Promise)[]) : Promise => { +const chainPromises = async ( + promiseFactories: ((index?: number) => Promise)[] +): Promise => { for (let index = 0; index < promiseFactories.length; index++) { await promiseFactories[index](index); }