From 266301e36b3349d1e5dc9bd3f95cd45ea27b264c Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Mon, 22 Jul 2024 13:12:42 +0200 Subject: [PATCH 1/3] chore(maintenance): add biome.js --- biome.json | 26 ++++++++ package-lock.json | 156 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 1 + 3 files changed, 183 insertions(+) create mode 100644 biome.json diff --git a/biome.json b/biome.json new file mode 100644 index 0000000000..19d23a030d --- /dev/null +++ b/biome.json @@ -0,0 +1,26 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.8.3/schema.json", + "organizeImports": { + "enabled": true + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true + } + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 2, + "lineEnding": "lf" + }, + "javascript": { + "formatter": { + "semicolons": "always", + "bracketSpacing": true, + "quoteStyle": "single", + "trailingComma": "es5" + } + } +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index ea890dc6e3..80d5733127 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,6 +24,7 @@ "examples/app" ], "devDependencies": { + "@biomejs/biome": "^1.8.3", "@types/aws-lambda": "^8.10.141", "@types/jest": "^29.5.12", "@types/node": "^20.14.10", @@ -1848,6 +1849,161 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "node_modules/@biomejs/biome": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-1.8.3.tgz", + "integrity": "sha512-/uUV3MV+vyAczO+vKrPdOW0Iaet7UnJMU4bNMinggGJTAnBPjCoLEYcyYtYHNnUNYlv4xZMH6hVIQCAozq8d5w==", + "dev": true, + "hasInstallScript": true, + "bin": { + "biome": "bin/biome" + }, + "engines": { + "node": ">=14.21.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/biome" + }, + "optionalDependencies": { + "@biomejs/cli-darwin-arm64": "1.8.3", + "@biomejs/cli-darwin-x64": "1.8.3", + "@biomejs/cli-linux-arm64": "1.8.3", + "@biomejs/cli-linux-arm64-musl": "1.8.3", + "@biomejs/cli-linux-x64": "1.8.3", + "@biomejs/cli-linux-x64-musl": "1.8.3", + "@biomejs/cli-win32-arm64": "1.8.3", + "@biomejs/cli-win32-x64": "1.8.3" + } + }, + "node_modules/@biomejs/cli-darwin-arm64": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.8.3.tgz", + "integrity": "sha512-9DYOjclFpKrH/m1Oz75SSExR8VKvNSSsLnVIqdnKexj6NwmiMlKk94Wa1kZEdv6MCOHGHgyyoV57Cw8WzL5n3A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-darwin-x64": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.8.3.tgz", + "integrity": "sha512-UeW44L/AtbmOF7KXLCoM+9PSgPo0IDcyEUfIoOXYeANaNXXf9mLUwV1GeF2OWjyic5zj6CnAJ9uzk2LT3v/wAw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-arm64": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.8.3.tgz", + "integrity": "sha512-fed2ji8s+I/m8upWpTJGanqiJ0rnlHOK3DdxsyVLZQ8ClY6qLuPc9uehCREBifRJLl/iJyQpHIRufLDeotsPtw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-arm64-musl": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.8.3.tgz", + "integrity": "sha512-9yjUfOFN7wrYsXt/T/gEWfvVxKlnh3yBpnScw98IF+oOeCYb5/b/+K7YNqKROV2i1DlMjg9g/EcN9wvj+NkMuQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-x64": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-1.8.3.tgz", + "integrity": "sha512-I8G2QmuE1teISyT8ie1HXsjFRz9L1m5n83U1O6m30Kw+kPMPSKjag6QGUn+sXT8V+XWIZxFFBoTDEDZW2KPDDw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-x64-musl": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-1.8.3.tgz", + "integrity": "sha512-UHrGJX7PrKMKzPGoEsooKC9jXJMa28TUSMjcIlbDnIO4EAavCoVmNQaIuUSH0Ls2mpGMwUIf+aZJv657zfWWjA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-win32-arm64": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.8.3.tgz", + "integrity": "sha512-J+Hu9WvrBevfy06eU1Na0lpc7uR9tibm9maHynLIoAjLZpQU3IW+OKHUtyL8p6/3pT2Ju5t5emReeIS2SAxhkQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-win32-x64": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-1.8.3.tgz", + "integrity": "sha512-/PJ59vA1pnQeKahemaQf4Nyj7IKUvGQSc3Ze1uIGi+Wvr1xF7rGobSrAAG01T/gUDG21vkDsZYM03NAmPiVkqg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.21.3" + } + }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", diff --git a/package.json b/package.json index 66fcd8ef7d..4efd18859e 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ }, "homepage": "https://github.com/aws-powertools/powertools-lambda-typescript#readme", "devDependencies": { + "@biomejs/biome": "^1.8.3", "@types/aws-lambda": "^8.10.141", "@types/jest": "^29.5.12", "@types/node": "^20.14.10", From 0ed3e1c55fb81bf175a5129cfab99c4109861dfb Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Mon, 22 Jul 2024 15:52:41 +0200 Subject: [PATCH 2/3] chore(maintenance): add biome to project --- biome.json | 15 +++++++++++++-- examples/app/cdk/example-stack.ts | 4 ++-- .../app/functions/commons/helpers/get-item.ts | 4 ++-- .../app/functions/commons/helpers/put-item.ts | 6 +++--- .../app/functions/commons/helpers/scan-items.ts | 4 ++-- examples/app/package.json | 10 +++------- package.json | 4 ++-- 7 files changed, 27 insertions(+), 20 deletions(-) diff --git a/biome.json b/biome.json index 19d23a030d..46c5a0c5d5 100644 --- a/biome.json +++ b/biome.json @@ -13,14 +13,25 @@ "enabled": true, "indentStyle": "space", "indentWidth": 2, - "lineEnding": "lf" + "lineEnding": "lf", + "lineWidth": 80 }, "javascript": { "formatter": { "semicolons": "always", "bracketSpacing": true, "quoteStyle": "single", - "trailingComma": "es5" + "trailingCommas": "es5" } + }, + "files": { + "ignore": [ + "node_modules", + "coverage", + "lib", + "cdk.out", + "site", + ".aws-sam" + ] } } \ No newline at end of file diff --git a/examples/app/cdk/example-stack.ts b/examples/app/cdk/example-stack.ts index b5e7f66284..067ef6752f 100644 --- a/examples/app/cdk/example-stack.ts +++ b/examples/app/cdk/example-stack.ts @@ -1,4 +1,4 @@ -import { RemovalPolicy, Stack, StackProps } from 'aws-cdk-lib'; +import { RemovalPolicy, Stack, type StackProps } from 'aws-cdk-lib'; import { LambdaIntegration, RestApi } from 'aws-cdk-lib/aws-apigateway'; import { AttributeType, @@ -18,7 +18,7 @@ import { DynamoEventSource } from 'aws-cdk-lib/aws-lambda-event-sources'; import { OutputFormat } from 'aws-cdk-lib/aws-lambda-nodejs'; import { Queue } from 'aws-cdk-lib/aws-sqs'; import { StringParameter } from 'aws-cdk-lib/aws-ssm'; -import { Construct } from 'constructs'; +import type { Construct } from 'constructs'; import { FunctionWithLogGroup } from './function-with-logstream-construct.js'; export class PowertoolsExampleStack extends Stack { diff --git a/examples/app/functions/commons/helpers/get-item.ts b/examples/app/functions/commons/helpers/get-item.ts index 3ce1030ea7..165b01f0d0 100644 --- a/examples/app/functions/commons/helpers/get-item.ts +++ b/examples/app/functions/commons/helpers/get-item.ts @@ -1,7 +1,7 @@ +import { GetCommand, type GetCommandOutput } from '@aws-sdk/lib-dynamodb'; import { docClient } from '#clients/dynamodb'; import { itemsTableName } from '#constants'; import type { DebugLogger } from '#types'; -import { GetCommand, type GetCommandOutput } from '@aws-sdk/lib-dynamodb'; /** * Fetch an item from the DynamoDB table. @@ -22,7 +22,7 @@ const getItemDynamoDB = async ( }) ); - logger.debug(`ddb response`, { + logger.debug('ddb response', { response, }); diff --git a/examples/app/functions/commons/helpers/put-item.ts b/examples/app/functions/commons/helpers/put-item.ts index a978e0382c..853756899d 100644 --- a/examples/app/functions/commons/helpers/put-item.ts +++ b/examples/app/functions/commons/helpers/put-item.ts @@ -1,8 +1,8 @@ +import { randomUUID } from 'node:crypto'; +import { PutCommand } from '@aws-sdk/lib-dynamodb'; import { docClient } from '#clients/dynamodb'; import { itemsTableName } from '#constants'; import type { DebugLogger } from '#types'; -import { PutCommand } from '@aws-sdk/lib-dynamodb'; -import { randomUUID } from 'node:crypto'; /** * Put an item in the DynamoDB table. @@ -28,7 +28,7 @@ const putItemInDynamoDB = async ( }) ); - logger.debug(`ddb response`, { + logger.debug('ddb response', { response, }); diff --git a/examples/app/functions/commons/helpers/scan-items.ts b/examples/app/functions/commons/helpers/scan-items.ts index 858b752c1d..d528085f28 100644 --- a/examples/app/functions/commons/helpers/scan-items.ts +++ b/examples/app/functions/commons/helpers/scan-items.ts @@ -1,7 +1,7 @@ +import { ScanCommand, type ScanCommandOutput } from '@aws-sdk/lib-dynamodb'; import { docClient } from '#clients/dynamodb'; import { itemsTableName } from '#constants'; import type { DebugLogger } from '#types'; -import { ScanCommand, type ScanCommandOutput } from '@aws-sdk/lib-dynamodb'; /** * Scan the DynamoDB table and return all items. @@ -19,7 +19,7 @@ const scanItemsDynamoDB = async ( }) ); - logger.debug(`ddb response`, { + logger.debug('ddb response', { response, }); diff --git a/examples/app/package.json b/examples/app/package.json index 30b84d438e..8f6c790d63 100644 --- a/examples/app/package.json +++ b/examples/app/package.json @@ -11,16 +11,12 @@ "scripts": { "build": "echo 'Not applicable, run `npx cdk synth` instead to build the stack'", "test": "npm run test:unit", - "lint": "eslint --ext .ts,.js --no-error-on-unmatched-pattern .", - "lint-fix": "eslint --fix --ext .ts,.js --fix --no-error-on-unmatched-pattern .", + "lint": "biome lint .", + "lint:fix": "biome check --write .", "test:unit": "export POWERTOOLS_DEV=true && jest --silent", "test:e2e": "echo 'To be implemented ...'", "cdk": "cdk" }, - "lint-staged": { - "*.ts": "npm run lint-fix", - "*.js": "npm run lint-fix" - }, "type": "module", "imports": { "#types": "./functions/commons/types.js", @@ -64,4 +60,4 @@ "ts-node": "^10.9.2", "typescript": "^5.4.5" } -} +} \ No newline at end of file diff --git a/package.json b/package.json index 4efd18859e..c1f422af13 100644 --- a/package.json +++ b/package.json @@ -78,7 +78,7 @@ "typescript": "^5.4.5" }, "lint-staged": { - "*.{js,ts}": "eslint --fix", + "*.{js,ts}": "biome check --write", "*.md": "markdownlint-cli2 --fix" }, "engines": { @@ -89,4 +89,4 @@ "tar": "6.2.1" } } -} +} \ No newline at end of file From 9d2e9918949e8faba1a8b3574f9b567c275fb44e Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Mon, 22 Jul 2024 17:00:00 +0200 Subject: [PATCH 3/3] chore(maintenance): migrate idempotency utility to biome --- packages/idempotency/package.json | 4 +- .../idempotency/src/IdempotencyHandler.ts | 53 ++++++++-------- packages/idempotency/src/deepSort.ts | 5 +- .../idempotency/src/idempotencyDecorator.ts | 5 +- packages/idempotency/src/makeIdempotent.ts | 10 ++-- .../src/persistence/BasePersistenceLayer.ts | 60 ++++++++++--------- .../persistence/DynamoDBPersistenceLayer.ts | 24 ++++---- .../src/persistence/IdempotencyRecord.ts | 10 ++-- .../idempotency/src/persistence/LRUCache.ts | 6 +- .../src/types/BasePersistenceLayer.ts | 4 +- .../src/types/IdempotencyOptions.ts | 8 +-- .../src/types/IdempotencyRecord.ts | 2 +- .../idempotentDecorator.test.FunctionCode.ts | 8 +-- ...makeHandlerIdempotent.test.FunctionCode.ts | 10 ++-- .../tests/e2e/makeHandlerIdempotent.test.ts | 8 +-- .../tests/e2e/makeIdempotent.test.ts | 8 +-- .../unit/EnvironmentVariableService.test.ts | 2 +- .../tests/unit/idempotencyDecorator.test.ts | 26 ++++---- 18 files changed, 127 insertions(+), 126 deletions(-) diff --git a/packages/idempotency/package.json b/packages/idempotency/package.json index cdf519798a..62fe41620a 100644 --- a/packages/idempotency/package.json +++ b/packages/idempotency/package.json @@ -20,8 +20,8 @@ "build:cjs": "tsc --build tsconfig.json && echo '{ \"type\": \"commonjs\" }' > lib/cjs/package.json", "build:esm": "tsc --build tsconfig.esm.json && echo '{ \"type\": \"module\" }' > lib/esm/package.json", "build": "npm run build:esm & npm run build:cjs", - "lint": "eslint --ext .ts,.js --no-error-on-unmatched-pattern .", - "lint-fix": "eslint --fix --ext .ts,.js --no-error-on-unmatched-pattern .", + "lint": "biome lint .", + "lint:fix": "biome check --write .", "prepack": "node ../../.github/scripts/release_patch_package_json.js ." }, "homepage": "https://github.com/aws-powertools/powertools-lambda-typescript/tree/main/packages/idempotency#readme", diff --git a/packages/idempotency/src/IdempotencyHandler.ts b/packages/idempotency/src/IdempotencyHandler.ts index df92a1d7fc..ab2903063d 100644 --- a/packages/idempotency/src/IdempotencyHandler.ts +++ b/packages/idempotency/src/IdempotencyHandler.ts @@ -1,24 +1,24 @@ -import type { Handler } from 'aws-lambda'; import type { JSONValue, MiddyLikeRequest, } from '@aws-lambda-powertools/commons/types'; -import type { - AnyFunction, - IdempotencyHandlerOptions, -} from './types/IdempotencyOptions.js'; +import { search } from '@aws-lambda-powertools/jmespath'; +import type { Handler } from 'aws-lambda'; +import type { IdempotencyConfig } from './IdempotencyConfig.js'; +import { IdempotencyRecordStatus, MAX_RETRIES } from './constants.js'; import { IdempotencyAlreadyInProgressError, IdempotencyInconsistentStateError, - IdempotencyItemAlreadyExistsError, + type IdempotencyItemAlreadyExistsError, IdempotencyPersistenceLayerError, IdempotencyUnknownError, } from './errors.js'; -import { BasePersistenceLayer } from './persistence/BasePersistenceLayer.js'; -import { IdempotencyRecord } from './persistence/IdempotencyRecord.js'; -import { IdempotencyConfig } from './IdempotencyConfig.js'; -import { MAX_RETRIES, IdempotencyRecordStatus } from './constants.js'; -import { search } from '@aws-lambda-powertools/jmespath'; +import type { BasePersistenceLayer } from './persistence/BasePersistenceLayer.js'; +import type { IdempotencyRecord } from './persistence/IdempotencyRecord.js'; +import type { + AnyFunction, + IdempotencyHandlerOptions, +} from './types/IdempotencyOptions.js'; /** * @internal @@ -99,9 +99,8 @@ export class IdempotencyHandler { throw new IdempotencyInconsistentStateError( 'Item has expired during processing and may not longer be valid.' ); - } else if ( - idempotencyRecord.getStatus() === IdempotencyRecordStatus.INPROGRESS - ) { + } + if (idempotencyRecord.getStatus() === IdempotencyRecordStatus.INPROGRESS) { if ( idempotencyRecord.inProgressExpiryTimestamp && idempotencyRecord.inProgressExpiryTimestamp < @@ -110,11 +109,10 @@ export class IdempotencyHandler { throw new IdempotencyInconsistentStateError( 'Item is in progress but the in progress expiry timestamp has expired.' ); - } else { - throw new IdempotencyAlreadyInProgressError( - `There is already an execution in progress with idempotency key: ${idempotencyRecord.idempotencyKey}` - ); } + throw new IdempotencyAlreadyInProgressError( + `There is already an execution in progress with idempotency key: ${idempotencyRecord.idempotencyKey}` + ); } return idempotencyRecord.getResponse(); @@ -129,7 +127,7 @@ export class IdempotencyHandler { * @returns The result of the function execution */ public async getFunctionResult(): Promise> { - let result; + let result: ReturnType; try { result = await this.#functionToMakeIdempotent.apply( this.#thisArg, @@ -168,7 +166,7 @@ export class IdempotencyHandler { ); } - let e; + let e: Error | undefined; for (let retryNo = 0; retryNo <= MAX_RETRIES; retryNo++) { try { const { isIdempotent, result } = @@ -234,7 +232,7 @@ export class IdempotencyHandler { public async handleMiddyBefore( request: MiddyLikeRequest, callback: (request: MiddyLikeRequest) => Promise - ): Promise | void> { + ): Promise | undefined> { for (let retryNo = 0; retryNo <= MAX_RETRIES; retryNo++) { try { const { isIdempotent, result } = @@ -309,9 +307,9 @@ export class IdempotencyHandler { ); return selection === undefined || selection === null; - } else { - return false; } + + return false; } /** @@ -388,12 +386,11 @@ export class IdempotencyHandler { ); return returnValue; - } else { - throw new IdempotencyPersistenceLayerError( - 'Failed to save in progress record to idempotency store', - { cause: error } - ); } + throw new IdempotencyPersistenceLayerError( + 'Failed to save in progress record to idempotency store', + { cause: error } + ); } }; diff --git a/packages/idempotency/src/deepSort.ts b/packages/idempotency/src/deepSort.ts index 29e052b1a4..4f190b01ca 100644 --- a/packages/idempotency/src/deepSort.ts +++ b/packages/idempotency/src/deepSort.ts @@ -1,5 +1,5 @@ import { getType } from '@aws-lambda-powertools/commons'; -import { +import type { JSONArray, JSONObject, JSONValue, @@ -36,7 +36,8 @@ const deepSort = (data: JSONValue): JSONValue => { const type = getType(data); if (type === 'object') { return sortObject(data as JSONObject); - } else if (type === 'array') { + } + if (type === 'array') { return (data as JSONArray).map(deepSort); } diff --git a/packages/idempotency/src/idempotencyDecorator.ts b/packages/idempotency/src/idempotencyDecorator.ts index 00c10690cb..b997237486 100644 --- a/packages/idempotency/src/idempotencyDecorator.ts +++ b/packages/idempotency/src/idempotencyDecorator.ts @@ -1,9 +1,9 @@ import type { Handler } from 'aws-lambda'; -import { +import { makeIdempotent } from './makeIdempotent.js'; +import type { AnyFunction, ItempotentFunctionOptions, } from './types/IdempotencyOptions.js'; -import { makeIdempotent } from './makeIdempotent.js'; /** * Use this decorator to make your lambda handler itempotent. @@ -60,6 +60,7 @@ const idempotent = function ( propertyKey: string, descriptor: PropertyDescriptor ) => PropertyDescriptor { + // biome-ignore lint/complexity/useArrowFunction: this is a decorator function and we need to maintain the `this` context return function ( _target: unknown, _propertyKey: string, diff --git a/packages/idempotency/src/makeIdempotent.ts b/packages/idempotency/src/makeIdempotent.ts index ed02a64f44..47aa1f3f7d 100644 --- a/packages/idempotency/src/makeIdempotent.ts +++ b/packages/idempotency/src/makeIdempotent.ts @@ -1,11 +1,12 @@ +import type { JSONValue } from '@aws-lambda-powertools/commons/types'; import type { Context, Handler } from 'aws-lambda'; +import { IdempotencyConfig } from './IdempotencyConfig.js'; +import { IdempotencyHandler } from './IdempotencyHandler.js'; import type { AnyFunction, - ItempotentFunctionOptions, IdempotencyLambdaHandlerOptions, + ItempotentFunctionOptions, } from './types/IdempotencyOptions.js'; -import { IdempotencyHandler } from './IdempotencyHandler.js'; -import { IdempotencyConfig } from './IdempotencyConfig.js'; const isContext = (arg: unknown): arg is Context => { return ( @@ -72,7 +73,6 @@ const isOptionsWithDataIndexArgument = ( * * ``` */ -// eslint-disable-next-line func-style function makeIdempotent( fn: Func, options: ItempotentFunctionOptions> @@ -83,7 +83,7 @@ function makeIdempotent( if (!idempotencyConfig.isEnabled()) return fn; return function (this: Handler, ...args: Parameters): ReturnType { - let functionPayloadToBeHashed; + let functionPayloadToBeHashed: JSONValue; if (isFnHandler(fn, args)) { idempotencyConfig.registerLambdaContext(args[1]); diff --git a/packages/idempotency/src/persistence/BasePersistenceLayer.ts b/packages/idempotency/src/persistence/BasePersistenceLayer.ts index 8c6ea5a363..83c730c72f 100644 --- a/packages/idempotency/src/persistence/BasePersistenceLayer.ts +++ b/packages/idempotency/src/persistence/BasePersistenceLayer.ts @@ -1,21 +1,21 @@ -import { createHash, Hash } from 'node:crypto'; +import { type Hash, createHash } from 'node:crypto'; +import type { JSONValue } from '@aws-lambda-powertools/commons/types'; import { search } from '@aws-lambda-powertools/jmespath'; import type { JMESPathParsingOptions } from '@aws-lambda-powertools/jmespath/types'; -import type { - BasePersistenceLayerOptions, - BasePersistenceLayerInterface, -} from '../types/BasePersistenceLayer.js'; -import { IdempotencyRecordStatus } from '../constants.js'; import { EnvironmentVariablesService } from '../config/EnvironmentVariablesService.js'; -import { IdempotencyRecord } from './IdempotencyRecord.js'; +import { IdempotencyRecordStatus } from '../constants.js'; +import { deepSort } from '../deepSort.js'; import { IdempotencyItemAlreadyExistsError, IdempotencyKeyError, IdempotencyValidationError, } from '../errors.js'; +import type { + BasePersistenceLayerInterface, + BasePersistenceLayerOptions, +} from '../types/BasePersistenceLayer.js'; +import { IdempotencyRecord } from './IdempotencyRecord.js'; import { LRUCache } from './LRUCache.js'; -import type { JSONValue } from '@aws-lambda-powertools/commons/types'; -import { deepSort } from '../deepSort.js'; /** * Base class for all persistence layers. This class provides the basic functionality for @@ -282,15 +282,15 @@ abstract class BasePersistenceLayer implements BasePersistenceLayerInterface { * @returns the idempotency key */ private getHashedIdempotencyKey(data: JSONValue): string { - if (this.eventKeyJmesPath) { - data = search( - this.eventKeyJmesPath, - data, - this.#jmesPathOptions - ) as JSONValue; - } - - if (BasePersistenceLayer.isMissingIdempotencyKey(data)) { + const payload = this.eventKeyJmesPath + ? (search( + this.eventKeyJmesPath, + data, + this.#jmesPathOptions + ) as JSONValue) + : data; + + if (BasePersistenceLayer.isMissingIdempotencyKey(payload)) { if (this.throwOnNoIdempotencyKey) { throw new IdempotencyKeyError( 'No data found to create a hashed idempotency_key' @@ -302,7 +302,7 @@ abstract class BasePersistenceLayer implements BasePersistenceLayerInterface { } return `${this.idempotencyKeyPrefix}#${this.generateHash( - JSON.stringify(deepSort(data)) + JSON.stringify(deepSort(payload)) )}`; } @@ -313,16 +313,20 @@ abstract class BasePersistenceLayer implements BasePersistenceLayerInterface { */ private getHashedPayload(data: JSONValue): string { if (this.isPayloadValidationEnabled() && this.validationKeyJmesPath) { - data = search( - this.validationKeyJmesPath, - data, - this.#jmesPathOptions - ) as JSONValue; - - return this.generateHash(JSON.stringify(deepSort(data))); - } else { - return ''; + return this.generateHash( + JSON.stringify( + deepSort( + search( + this.validationKeyJmesPath, + data, + this.#jmesPathOptions + ) as JSONValue + ) + ) + ); } + + return ''; } private static isMissingIdempotencyKey(data: JSONValue): boolean { diff --git a/packages/idempotency/src/persistence/DynamoDBPersistenceLayer.ts b/packages/idempotency/src/persistence/DynamoDBPersistenceLayer.ts index 246dad0284..37b75016cc 100644 --- a/packages/idempotency/src/persistence/DynamoDBPersistenceLayer.ts +++ b/packages/idempotency/src/persistence/DynamoDBPersistenceLayer.ts @@ -1,26 +1,26 @@ import { - IdempotencyItemAlreadyExistsError, - IdempotencyItemNotFoundError, -} from '../errors.js'; -import { IdempotencyRecordStatus } from '../constants.js'; -import type { DynamoDBPersistenceOptions } from '../types/DynamoDBPersistence.js'; + addUserAgentMiddleware, + isSdkClient, +} from '@aws-lambda-powertools/commons'; import { - AttributeValue, + type AttributeValue, ConditionalCheckFailedException, DeleteItemCommand, DynamoDBClient, - DynamoDBClientConfig, + type DynamoDBClientConfig, GetItemCommand, PutItemCommand, UpdateItemCommand, } from '@aws-sdk/client-dynamodb'; import { marshall, unmarshall } from '@aws-sdk/util-dynamodb'; -import { IdempotencyRecord } from './IdempotencyRecord.js'; -import { BasePersistenceLayer } from './BasePersistenceLayer.js'; +import { IdempotencyRecordStatus } from '../constants.js'; import { - addUserAgentMiddleware, - isSdkClient, -} from '@aws-lambda-powertools/commons'; + IdempotencyItemAlreadyExistsError, + IdempotencyItemNotFoundError, +} from '../errors.js'; +import type { DynamoDBPersistenceOptions } from '../types/DynamoDBPersistence.js'; +import { BasePersistenceLayer } from './BasePersistenceLayer.js'; +import { IdempotencyRecord } from './IdempotencyRecord.js'; /** * DynamoDB persistence layer for idempotency records. diff --git a/packages/idempotency/src/persistence/IdempotencyRecord.ts b/packages/idempotency/src/persistence/IdempotencyRecord.ts index 3dba4af73c..c5057e846f 100644 --- a/packages/idempotency/src/persistence/IdempotencyRecord.ts +++ b/packages/idempotency/src/persistence/IdempotencyRecord.ts @@ -1,10 +1,10 @@ import type { JSONValue } from '@aws-lambda-powertools/commons/types'; +import { IdempotencyRecordStatus } from '../constants.js'; +import { IdempotencyInvalidStatusError } from '../errors.js'; import type { IdempotencyRecordOptions, IdempotencyRecordStatusValue, } from '../types/IdempotencyRecord.js'; -import { IdempotencyRecordStatus } from '../constants.js'; -import { IdempotencyInvalidStatusError } from '../errors.js'; /** * Class representing an idempotency record. @@ -62,11 +62,11 @@ class IdempotencyRecord { public getStatus(): IdempotencyRecordStatusValue { if (this.isExpired()) { return IdempotencyRecordStatus.EXPIRED; - } else if (Object.values(IdempotencyRecordStatus).includes(this.status)) { + } + if (Object.values(IdempotencyRecordStatus).includes(this.status)) { return this.status; - } else { - throw new IdempotencyInvalidStatusError(this.status); } + throw new IdempotencyInvalidStatusError(this.status); } /** diff --git a/packages/idempotency/src/persistence/LRUCache.ts b/packages/idempotency/src/persistence/LRUCache.ts index 45b8630d4a..c41f07e1a6 100644 --- a/packages/idempotency/src/persistence/LRUCache.ts +++ b/packages/idempotency/src/persistence/LRUCache.ts @@ -97,8 +97,7 @@ class LRUCache { public add(key: K, value: V): void { // If the key already exists, we just update the value and mark it as the most recently used if (this.map.has(key)) { - // At this point, we know that the key exists in the map, so we can safely use the non-null - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + // biome-ignore lint/style/noNonNullAssertion: At this point, we know that the key exists in the map, so we can safely use the non-null const item = this.map.get(key)!; item.value = value; this.trackItemUse(item); @@ -192,8 +191,7 @@ class LRUCache { * Removes the oldest item from the cache and unlinks it from the linked list. */ private shift(): void { - // If this function is called, we know that the least recently used item exists - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + // biome-ignore lint/style/noNonNullAssertion: If this function is called, we know that the least recently used item exists const item = this.leastRecentlyUsed!; // If there's a newer item, make it the oldest diff --git a/packages/idempotency/src/types/BasePersistenceLayer.ts b/packages/idempotency/src/types/BasePersistenceLayer.ts index badda0271f..a37440873e 100644 --- a/packages/idempotency/src/types/BasePersistenceLayer.ts +++ b/packages/idempotency/src/types/BasePersistenceLayer.ts @@ -1,5 +1,5 @@ -import { IdempotencyRecord } from '../persistence/IdempotencyRecord.js'; -import { IdempotencyConfig } from '../IdempotencyConfig.js'; +import type { IdempotencyConfig } from '../IdempotencyConfig.js'; +import type { IdempotencyRecord } from '../persistence/IdempotencyRecord.js'; type BasePersistenceLayerOptions = { config: IdempotencyConfig; diff --git a/packages/idempotency/src/types/IdempotencyOptions.ts b/packages/idempotency/src/types/IdempotencyOptions.ts index e1c290e233..3860c1f7eb 100644 --- a/packages/idempotency/src/types/IdempotencyOptions.ts +++ b/packages/idempotency/src/types/IdempotencyOptions.ts @@ -1,7 +1,7 @@ +import type { JSONValue } from '@aws-lambda-powertools/commons/types'; import type { Context, Handler } from 'aws-lambda'; -import type { BasePersistenceLayer } from '../persistence/BasePersistenceLayer.js'; import type { IdempotencyConfig } from '../IdempotencyConfig.js'; -import type { JSONValue } from '@aws-lambda-powertools/commons/types'; +import type { BasePersistenceLayer } from '../persistence/BasePersistenceLayer.js'; /** * Configuration options for the idempotency utility. @@ -23,7 +23,7 @@ type IdempotencyLambdaHandlerOptions = { * * It's left intentionally open to allow for any function to be wrapped. */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any +// biome-ignore lint/suspicious/noExplicitAny: This is a generic type that is intentionally open type AnyFunction = (...args: Array) => any; /** @@ -99,7 +99,7 @@ type AnyFunction = (...args: Array) => any; * the `eventKeyJmesPath` property to indicate the JMESPath expression to use to extract part of the payload. * */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any +// biome-ignore lint/suspicious/noExplicitAny: This is a generic type that is intentionally open type ItempotentFunctionOptions> = T[1] extends Context ? IdempotencyLambdaHandlerOptions : IdempotencyLambdaHandlerOptions & { diff --git a/packages/idempotency/src/types/IdempotencyRecord.ts b/packages/idempotency/src/types/IdempotencyRecord.ts index 486cb2561d..565ad8dac5 100644 --- a/packages/idempotency/src/types/IdempotencyRecord.ts +++ b/packages/idempotency/src/types/IdempotencyRecord.ts @@ -1,5 +1,5 @@ import type { JSONValue } from '@aws-lambda-powertools/commons/types'; -import { IdempotencyRecordStatus } from '../constants.js'; +import type { IdempotencyRecordStatus } from '../constants.js'; type IdempotencyRecordStatusValue = (typeof IdempotencyRecordStatus)[keyof typeof IdempotencyRecordStatus]; diff --git a/packages/idempotency/tests/e2e/idempotentDecorator.test.FunctionCode.ts b/packages/idempotency/tests/e2e/idempotentDecorator.test.FunctionCode.ts index 603bff9208..9063d652cd 100644 --- a/packages/idempotency/tests/e2e/idempotentDecorator.test.FunctionCode.ts +++ b/packages/idempotency/tests/e2e/idempotentDecorator.test.FunctionCode.ts @@ -1,9 +1,9 @@ -import type { Context } from 'aws-lambda'; import type { LambdaInterface } from '@aws-lambda-powertools/commons/types'; -import { idempotent } from '../../src/idempotencyDecorator'; import { Logger } from '@aws-lambda-powertools/logger'; -import { DynamoDBPersistenceLayer } from '../../src/persistence/DynamoDBPersistenceLayer.js'; +import type { Context } from 'aws-lambda'; import { IdempotencyConfig } from '../../src/IdempotencyConfig.js'; +import { idempotent } from '../../src/idempotencyDecorator'; +import { DynamoDBPersistenceLayer } from '../../src/persistence/DynamoDBPersistenceLayer.js'; const IDEMPOTENCY_TABLE_NAME = process.env.IDEMPOTENCY_TABLE_NAME || 'table_name'; @@ -148,7 +148,7 @@ class LambdaWithKeywordArgument implements LambdaInterface { public async process(id: string, foo: string): Promise { logger.info('Got test event', { id, foo }); - return 'idempotent result: ' + foo; + return `idempotent result: ${foo}`; } } diff --git a/packages/idempotency/tests/e2e/makeHandlerIdempotent.test.FunctionCode.ts b/packages/idempotency/tests/e2e/makeHandlerIdempotent.test.FunctionCode.ts index b4aa178d56..44addbf10f 100644 --- a/packages/idempotency/tests/e2e/makeHandlerIdempotent.test.FunctionCode.ts +++ b/packages/idempotency/tests/e2e/makeHandlerIdempotent.test.FunctionCode.ts @@ -1,9 +1,9 @@ -import type { Context } from 'aws-lambda'; -import { DynamoDBPersistenceLayer } from '../../src/persistence/DynamoDBPersistenceLayer.js'; -import { makeHandlerIdempotent } from '../../src/middleware/makeHandlerIdempotent.js'; -import { IdempotencyConfig } from '../../src/IdempotencyConfig.js'; import { Logger } from '@aws-lambda-powertools/logger'; import middy from '@middy/core'; +import type { Context } from 'aws-lambda'; +import { IdempotencyConfig } from '../../src/IdempotencyConfig.js'; +import { makeHandlerIdempotent } from '../../src/middleware/makeHandlerIdempotent.js'; +import { DynamoDBPersistenceLayer } from '../../src/persistence/DynamoDBPersistenceLayer.js'; const IDEMPOTENCY_TABLE_NAME = process.env.IDEMPOTENCY_TABLE_NAME || 'table_name'; @@ -19,7 +19,7 @@ const logger = new Logger(); export const handler = middy( async (event: { foo: string }, context: Context) => { logger.addContext(context); - logger.info(`foo`, { details: event.foo }); + logger.info('foo', { details: event.foo }); return event.foo; } diff --git a/packages/idempotency/tests/e2e/makeHandlerIdempotent.test.ts b/packages/idempotency/tests/e2e/makeHandlerIdempotent.test.ts index bf67669ab6..fb13ad73d2 100644 --- a/packages/idempotency/tests/e2e/makeHandlerIdempotent.test.ts +++ b/packages/idempotency/tests/e2e/makeHandlerIdempotent.test.ts @@ -1,18 +1,18 @@ +import { createHash } from 'node:crypto'; +import { join } from 'node:path'; /** * Test makeHandlerIdempotent middleware * * @group e2e/idempotency/makeHandlerIdempotent */ import { - invokeFunction, TestInvocationLogs, TestStack, + invokeFunction, } from '@aws-lambda-powertools/testing-utils'; import { DynamoDBClient } from '@aws-sdk/client-dynamodb'; import { ScanCommand } from '@aws-sdk/lib-dynamodb'; import { Duration } from 'aws-cdk-lib'; -import { createHash } from 'node:crypto'; -import { join } from 'node:path'; import { IdempotencyTestNodejsFunctionAndDynamoTable } from '../helpers/resources.js'; import { RESOURCE_NAME_PREFIX, @@ -23,7 +23,7 @@ import { const ddb = new DynamoDBClient({}); -describe(`Idempotency E2E tests, middy middleware usage`, () => { +describe('Idempotency E2E tests, middy middleware usage', () => { const testStack = new TestStack({ stackNameProps: { stackNamePrefix: RESOURCE_NAME_PREFIX, diff --git a/packages/idempotency/tests/e2e/makeIdempotent.test.ts b/packages/idempotency/tests/e2e/makeIdempotent.test.ts index 9c85dec710..910060d474 100644 --- a/packages/idempotency/tests/e2e/makeIdempotent.test.ts +++ b/packages/idempotency/tests/e2e/makeIdempotent.test.ts @@ -1,18 +1,18 @@ +import { createHash } from 'node:crypto'; +import { join } from 'node:path'; /** * Test makeIdempotent function * * @group e2e/idempotency/makeIdempotent */ import { - invokeFunction, TestInvocationLogs, TestStack, + invokeFunction, } from '@aws-lambda-powertools/testing-utils'; import { DynamoDBClient } from '@aws-sdk/client-dynamodb'; import { ScanCommand } from '@aws-sdk/lib-dynamodb'; import { AttributeType } from 'aws-cdk-lib/aws-dynamodb'; -import { createHash } from 'node:crypto'; -import { join } from 'node:path'; import { IdempotencyTestNodejsFunctionAndDynamoTable } from '../helpers/resources.js'; import { RESOURCE_NAME_PREFIX, @@ -21,7 +21,7 @@ import { TEST_CASE_TIMEOUT, } from './constants'; -describe(`Idempotency E2E tests, wrapper function usage`, () => { +describe('Idempotency E2E tests, wrapper function usage', () => { const testStack = new TestStack({ stackNameProps: { stackNamePrefix: RESOURCE_NAME_PREFIX, diff --git a/packages/idempotency/tests/unit/EnvironmentVariableService.test.ts b/packages/idempotency/tests/unit/EnvironmentVariableService.test.ts index a7176e7897..6681ef672c 100644 --- a/packages/idempotency/tests/unit/EnvironmentVariableService.test.ts +++ b/packages/idempotency/tests/unit/EnvironmentVariableService.test.ts @@ -31,7 +31,7 @@ describe('Class: EnvironmentVariableService', () => { test('When called without the environment variable set, it returns an empty string', () => { // Prepare - delete process.env.AWS_LAMBDA_FUNCTION_NAME; + process.env.AWS_LAMBDA_FUNCTION_NAME = undefined; // Act const lambdaName = new EnvironmentVariablesService().getFunctionName(); diff --git a/packages/idempotency/tests/unit/idempotencyDecorator.test.ts b/packages/idempotency/tests/unit/idempotencyDecorator.test.ts index db1353b6da..0ad36d10d3 100644 --- a/packages/idempotency/tests/unit/idempotencyDecorator.test.ts +++ b/packages/idempotency/tests/unit/idempotencyDecorator.test.ts @@ -1,3 +1,15 @@ +import type { LambdaInterface } from '@aws-lambda-powertools/commons/types'; +import context from '@aws-lambda-powertools/testing-utils/context'; +import type { Context } from 'aws-lambda'; +import { IdempotencyRecordStatus } from '../../src/constants.js'; +import { + IdempotencyAlreadyInProgressError, + IdempotencyConfig, + IdempotencyInconsistentStateError, + IdempotencyItemAlreadyExistsError, + IdempotencyPersistenceLayerError, + idempotent, +} from '../../src/index.js'; /** * Test Function Wrapper * @@ -7,19 +19,7 @@ import { BasePersistenceLayer, IdempotencyRecord, } from '../../src/persistence/index.js'; -import { - idempotent, - IdempotencyConfig, - IdempotencyAlreadyInProgressError, - IdempotencyInconsistentStateError, - IdempotencyItemAlreadyExistsError, - IdempotencyPersistenceLayerError, -} from '../../src/index.js'; import type { IdempotencyRecordOptions } from '../../src/types/index.js'; -import { Context } from 'aws-lambda'; -import context from '@aws-lambda-powertools/testing-utils/context'; -import { IdempotencyRecordStatus } from '../../src/constants.js'; -import type { LambdaInterface } from '@aws-lambda-powertools/commons/types'; const mockSaveInProgress = jest .spyOn(BasePersistenceLayer.prototype, 'saveInProgress') @@ -314,7 +314,7 @@ describe('Given a class with a function to decorate', (classWithLambdaHandler = }); afterAll(() => { - delete process.env.POWERTOOLS_IDEMPOTENCY_DISABLED; + process.env.POWERTOOLS_IDEMPOTENCY_DISABLED = undefined; }); });