diff --git a/examples/cdk/.eslintrc.js b/examples/cdk/.eslintrc.js new file mode 100644 index 0000000000..8cfae0905f --- /dev/null +++ b/examples/cdk/.eslintrc.js @@ -0,0 +1,72 @@ +module.exports = { + env: { + browser: false, + es2020: true, + jest: true, + node: true, + }, + ignorePatterns: ['cdk.out', '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/examples/cdk/bin/cdk-app.ts b/examples/cdk/bin/cdk-app.ts index 19b5cd2c5e..7142e425b9 100644 --- a/examples/cdk/bin/cdk-app.ts +++ b/examples/cdk/bin/cdk-app.ts @@ -4,4 +4,4 @@ import * as cdk from 'aws-cdk-lib'; import { CdkAppStack } from '../src/example-stack'; const app = new cdk.App(); -new CdkAppStack(app, 'LambdaPowertoolsTypeScript-ExamplesCdkStack', {}); \ No newline at end of file +new CdkAppStack(app, 'LambdaPowertoolsTypeScript-ExamplesCdkStack', {}); diff --git a/examples/cdk/functions/common/constants.ts b/examples/cdk/functions/common/constants.ts index 3a731e43d8..3496c50046 100644 --- a/examples/cdk/functions/common/constants.ts +++ b/examples/cdk/functions/common/constants.ts @@ -1,6 +1,4 @@ // Get the DynamoDB table name from environment variables const tableName = process.env.SAMPLE_TABLE; -export { - tableName -}; \ No newline at end of file +export { tableName }; diff --git a/examples/cdk/functions/common/dynamodb-client.ts b/examples/cdk/functions/common/dynamodb-client.ts index fe4e7cc8d5..40a7c994b8 100644 --- a/examples/cdk/functions/common/dynamodb-client.ts +++ b/examples/cdk/functions/common/dynamodb-client.ts @@ -24,6 +24,4 @@ const translateConfig = { marshallOptions, unmarshallOptions }; // Create the DynamoDB Document client. const docClient = DynamoDBDocumentClient.from(ddbClient, translateConfig); -export { - docClient -}; \ No newline at end of file +export { docClient }; diff --git a/examples/cdk/functions/common/powertools.ts b/examples/cdk/functions/common/powertools.ts index 0215708207..0b5a768326 100644 --- a/examples/cdk/functions/common/powertools.ts +++ b/examples/cdk/functions/common/powertools.ts @@ -6,7 +6,7 @@ const awsLambdaPowertoolsVersion = '1.5.0'; const defaultValues = { region: process.env.AWS_REGION || 'N/A', - executionEnv: process.env.AWS_EXECUTION_ENV || 'N/A' + executionEnv: process.env.AWS_EXECUTION_ENV || 'N/A', }; const logger = new Logger({ @@ -15,18 +15,14 @@ const logger = new Logger({ logger: { name: '@aws-lambda-powertools/logger', version: awsLambdaPowertoolsVersion, - } + }, }, }); const metrics = new Metrics({ - defaultDimensions: defaultValues + defaultDimensions: defaultValues, }); const tracer = new Tracer(); -export { - logger, - metrics, - tracer -}; \ No newline at end of file +export { logger, metrics, tracer }; diff --git a/examples/cdk/functions/get-all-items.ts b/examples/cdk/functions/get-all-items.ts index a84709356c..28c7bdbebd 100644 --- a/examples/cdk/functions/get-all-items.ts +++ b/examples/cdk/functions/get-all-items.ts @@ -1,4 +1,8 @@ -import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda'; +import { + APIGatewayProxyEvent, + APIGatewayProxyResult, + Context, +} from 'aws-lambda'; import middy from '@middy/core'; import { tableName } from './common/constants'; import { logger, tracer, metrics } from './common/powertools'; @@ -12,7 +16,7 @@ import { default as request } from 'phin'; /* * * This example uses the Middy middleware instrumentation. - * It is the best choice if your existing code base relies on the Middy middleware engine. + * It is the best choice if your existing code base relies on the Middy middleware engine. * Powertools offers compatible Middy middleware to make this integration seamless. * Find more Information in the docs: https://awslabs.github.io/aws-lambda-powertools-typescript/ * @@ -23,9 +27,14 @@ import { default as request } from 'phin'; * @returns {Object} object - API Gateway Lambda Proxy Output Format * */ -const getAllItemsHandler = async (event: APIGatewayProxyEvent, context: Context): Promise => { +const getAllItemsHandler = async ( + event: APIGatewayProxyEvent, + context: Context +): Promise => { if (event.httpMethod !== 'GET') { - throw new Error(`getAllItems only accepts GET method, you tried: ${event.httpMethod}`); + throw new Error( + `getAllItems only accepts GET method, you tried: ${event.httpMethod}` + ); } // Tracer: Add awsRequestId as annotation @@ -60,9 +69,11 @@ const getAllItemsHandler = async (event: APIGatewayProxyEvent, context: Context) throw new Error('SAMPLE_TABLE environment variable is not set'); } - const data = await docClient.send(new ScanCommand({ - TableName: tableName - })); + const data = await docClient.send( + new ScanCommand({ + TableName: tableName, + }) + ); const { Items: items } = data; // Logger: All log statements are written to CloudWatch @@ -75,7 +86,7 @@ const getAllItemsHandler = async (event: APIGatewayProxyEvent, context: Context) return { statusCode: 200, - body: JSON.stringify(items) + body: JSON.stringify(items), }; } catch (err) { tracer.addErrorAsMetadata(err as Error); @@ -83,7 +94,7 @@ const getAllItemsHandler = async (event: APIGatewayProxyEvent, context: Context) return { statusCode: 500, - body: JSON.stringify({ 'error': 'Error reading from table.' }) + body: JSON.stringify({ error: 'Error reading from table.' }), }; } }; @@ -95,4 +106,4 @@ export const handler = middy(getAllItemsHandler) // Use the middleware by passing the Logger instance as a parameter .use(injectLambdaContext(logger, { logEvent: true })) // Use the middleware by passing the Tracer instance as a parameter - .use(captureLambdaHandler(tracer, { captureResponse: false })); // by default the tracer would add the response as metadata on the segment, but there is a chance to hit the 64kb segment size limit. Therefore set captureResponse: false \ No newline at end of file + .use(captureLambdaHandler(tracer, { captureResponse: false })); // by default the tracer would add the response as metadata on the segment, but there is a chance to hit the 64kb segment size limit. Therefore set captureResponse: false diff --git a/examples/cdk/functions/get-by-id.ts b/examples/cdk/functions/get-by-id.ts index 430526a432..0a24f49632 100644 --- a/examples/cdk/functions/get-by-id.ts +++ b/examples/cdk/functions/get-by-id.ts @@ -1,4 +1,8 @@ -import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda'; +import { + APIGatewayProxyEvent, + APIGatewayProxyResult, + Context, +} from 'aws-lambda'; import { tableName } from './common/constants'; import { logger, tracer, metrics } from './common/powertools'; import { LambdaInterface } from '@aws-lambda-powertools/commons'; @@ -22,7 +26,6 @@ import { default as request } from 'phin'; */ class Lambda implements LambdaInterface { - @tracer.captureMethod() public async getUuid(): Promise { // Request a sample random uuid from a webservice @@ -37,11 +40,18 @@ class Lambda implements LambdaInterface { @tracer.captureLambdaHandler({ captureResponse: false }) // by default the tracer would add the response as metadata on the segment, but there is a chance to hit the 64kb segment size limit. Therefore set captureResponse: false @logger.injectLambdaContext({ logEvent: true }) - @metrics.logMetrics({ throwOnEmptyMetrics: false, captureColdStartMetric: true }) - public async handler(event: APIGatewayProxyEvent, context: Context): Promise { - + @metrics.logMetrics({ + throwOnEmptyMetrics: false, + captureColdStartMetric: true, + }) + public async handler( + event: APIGatewayProxyEvent, + context: Context + ): Promise { if (event.httpMethod !== 'GET') { - throw new Error(`getById only accepts GET method, you tried: ${event.httpMethod}`); + throw new Error( + `getById only accepts GET method, you tried: ${event.httpMethod}` + ); } // Tracer: Add awsRequestId as annotation @@ -76,12 +86,14 @@ class Lambda implements LambdaInterface { if (!event.pathParameters.id) { throw new Error('PathParameter id is missing'); } - const data = await docClient.send(new GetCommand({ - TableName: tableName, - Key: { - id: event.pathParameters.id - } - })); + const data = await docClient.send( + new GetCommand({ + TableName: tableName, + Key: { + id: event.pathParameters.id, + }, + }) + ); const item = data.Item; logger.info(`Response ${event.path}`, { @@ -91,7 +103,7 @@ class Lambda implements LambdaInterface { return { statusCode: 200, - body: JSON.stringify(item) + body: JSON.stringify(item), }; } catch (err) { tracer.addErrorAsMetadata(err as Error); @@ -99,11 +111,10 @@ class Lambda implements LambdaInterface { return { statusCode: 500, - body: JSON.stringify({ 'error': 'Error reading from table.' }) + body: JSON.stringify({ error: 'Error reading from table.' }), }; } } - } const handlerClass = new Lambda(); diff --git a/examples/cdk/functions/put-item.ts b/examples/cdk/functions/put-item.ts index 7521f5e896..c51b37c094 100644 --- a/examples/cdk/functions/put-item.ts +++ b/examples/cdk/functions/put-item.ts @@ -1,4 +1,8 @@ -import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda'; +import { + APIGatewayProxyEvent, + APIGatewayProxyResult, + Context, +} from 'aws-lambda'; import { tableName } from './common/constants'; import { logger, tracer, metrics } from './common/powertools'; import { docClient } from './common/dynamodb-client'; @@ -9,7 +13,7 @@ import type { Subsegment } from 'aws-xray-sdk-core'; /** * * This example uses the manual instrumentation. - * + * * Event doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format * @param {APIGatewayProxyEvent} event - API Gateway Lambda Proxy Input Format * @@ -17,9 +21,14 @@ import type { Subsegment } from 'aws-xray-sdk-core'; * @returns {Promise} object - API Gateway Lambda Proxy Output Format * */ -export const handler = async (event: APIGatewayProxyEvent, context: Context): Promise => { +export const handler = async ( + event: APIGatewayProxyEvent, + context: Context +): Promise => { if (event.httpMethod !== 'POST') { - throw new Error(`putItem only accepts POST method, you tried: ${event.httpMethod}`); + throw new Error( + `putItem only accepts POST method, you tried: ${event.httpMethod}` + ); } // Logger: Log the incoming event @@ -80,13 +89,15 @@ export const handler = async (event: APIGatewayProxyEvent, context: Context): Pr const body = JSON.parse(event.body); const { id, name } = body; - await docClient.send(new PutCommand({ - TableName: tableName, - Item: { - id, - name - } - })); + await docClient.send( + new PutCommand({ + TableName: tableName, + Item: { + id, + name, + }, + }) + ); logger.info(`Response ${event.path}`, { statusCode: 200, @@ -95,7 +106,7 @@ export const handler = async (event: APIGatewayProxyEvent, context: Context): Pr return { statusCode: 200, - body: JSON.stringify(body) + body: JSON.stringify(body), }; } catch (err) { tracer.addErrorAsMetadata(err as Error); @@ -103,7 +114,7 @@ export const handler = async (event: APIGatewayProxyEvent, context: Context): Pr return { statusCode: 500, - body: JSON.stringify({ 'error': 'Error writing data to table.' }) + body: JSON.stringify({ error: 'Error writing data to table.' }), }; } finally { if (segment && handlerSegment) { diff --git a/examples/cdk/jest.config.js b/examples/cdk/jest.config.js index e3795fc8b4..6cfd2c7be4 100644 --- a/examples/cdk/jest.config.js +++ b/examples/cdk/jest.config.js @@ -3,6 +3,6 @@ module.exports = { roots: ['/tests'], testMatch: ['**/*.test.ts'], transform: { - '^.+\\.tsx?$': 'ts-jest' - } + '^.+\\.tsx?$': 'ts-jest', + }, }; diff --git a/examples/cdk/package.json b/examples/cdk/package.json index c427f94577..0734907207 100644 --- a/examples/cdk/package.json +++ b/examples/cdk/package.json @@ -15,8 +15,8 @@ "build": "tsc --skipLibCheck", "watch": "tsc -w", "test": "npm run test:unit", - "lint": "eslint --ext .ts --no-error-on-unmatched-pattern src tests", - "lint-fix": "eslint --fix --ext .ts --fix --no-error-on-unmatched-pattern src tests", + "lint": "eslint --ext .ts,.js --no-error-on-unmatched-pattern .", + "lint-fix": "eslint --fix --ext .ts,.js --fix --no-error-on-unmatched-pattern .", "package": "echo 'Not applicable'", "package-bundle": "echo 'Not applicable'", "test:unit": "export POWERTOOLS_DEV=true && npm run build && jest --silent", diff --git a/examples/cdk/src/example-stack.ts b/examples/cdk/src/example-stack.ts index 9c9803ce6f..e79efc42b8 100644 --- a/examples/cdk/src/example-stack.ts +++ b/examples/cdk/src/example-stack.ts @@ -1,7 +1,10 @@ import { Stack, StackProps, Duration } from 'aws-cdk-lib'; import { Construct } from 'constructs'; import { Table, BillingMode, AttributeType } from 'aws-cdk-lib/aws-dynamodb'; -import { NodejsFunction, NodejsFunctionProps } from 'aws-cdk-lib/aws-lambda-nodejs'; +import { + NodejsFunction, + NodejsFunctionProps, +} from 'aws-cdk-lib/aws-lambda-nodejs'; import { Runtime, Tracing, LayerVersion } from 'aws-cdk-lib/aws-lambda'; import { RetentionDays } from 'aws-cdk-lib/aws-logs'; import { RestApi, LambdaIntegration } from 'aws-cdk-lib/aws-apigateway'; @@ -15,7 +18,7 @@ const commonProps: Partial = { NODE_OPTIONS: '--enable-source-maps', // see https://docs.aws.amazon.com/lambda/latest/dg/typescript-exceptions.html POWERTOOLS_SERVICE_NAME: 'items-store', POWERTOOLS_METRICS_NAMESPACE: 'PowertoolsCDKExample', - LOG_LEVEL: 'DEBUG' + LOG_LEVEL: 'DEBUG', }, bundling: { externalModules: [ @@ -39,14 +42,17 @@ export class CdkAppStack extends Stack { partitionKey: { type: AttributeType.STRING, name: 'id', - } + }, }); commonProps.layers?.push( LayerVersion.fromLayerVersionArn( this, 'powertools-layer', - `arn:aws:lambda:${Stack.of(this).region}:094274105915:layer:AWSLambdaPowertoolsTypeScript:6`) + `arn:aws:lambda:${ + Stack.of(this).region + }:094274105915:layer:AWSLambdaPowertoolsTypeScript:6` + ) ); const putItemFn = new NodejsFunction(this, 'put-item-fn', {