Skip to content

Commit 4312ca0

Browse files
authored
chore(maintenance): migrate idempotency utility to biome (#2810)
1 parent 23e1073 commit 4312ca0

18 files changed

+127
-126
lines changed

packages/idempotency/package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020
"build:cjs": "tsc --build tsconfig.json && echo '{ \"type\": \"commonjs\" }' > lib/cjs/package.json",
2121
"build:esm": "tsc --build tsconfig.esm.json && echo '{ \"type\": \"module\" }' > lib/esm/package.json",
2222
"build": "npm run build:esm & npm run build:cjs",
23-
"lint": "eslint --ext .ts,.js --no-error-on-unmatched-pattern .",
24-
"lint-fix": "eslint --fix --ext .ts,.js --no-error-on-unmatched-pattern .",
23+
"lint": "biome lint .",
24+
"lint:fix": "biome check --write .",
2525
"prepack": "node ../../.github/scripts/release_patch_package_json.js ."
2626
},
2727
"homepage": "https://github.com/aws-powertools/powertools-lambda-typescript/tree/main/packages/idempotency#readme",

packages/idempotency/src/IdempotencyHandler.ts

+25-28
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,24 @@
1-
import type { Handler } from 'aws-lambda';
21
import type {
32
JSONValue,
43
MiddyLikeRequest,
54
} from '@aws-lambda-powertools/commons/types';
6-
import type {
7-
AnyFunction,
8-
IdempotencyHandlerOptions,
9-
} from './types/IdempotencyOptions.js';
5+
import { search } from '@aws-lambda-powertools/jmespath';
6+
import type { Handler } from 'aws-lambda';
7+
import type { IdempotencyConfig } from './IdempotencyConfig.js';
8+
import { IdempotencyRecordStatus, MAX_RETRIES } from './constants.js';
109
import {
1110
IdempotencyAlreadyInProgressError,
1211
IdempotencyInconsistentStateError,
13-
IdempotencyItemAlreadyExistsError,
12+
type IdempotencyItemAlreadyExistsError,
1413
IdempotencyPersistenceLayerError,
1514
IdempotencyUnknownError,
1615
} from './errors.js';
17-
import { BasePersistenceLayer } from './persistence/BasePersistenceLayer.js';
18-
import { IdempotencyRecord } from './persistence/IdempotencyRecord.js';
19-
import { IdempotencyConfig } from './IdempotencyConfig.js';
20-
import { MAX_RETRIES, IdempotencyRecordStatus } from './constants.js';
21-
import { search } from '@aws-lambda-powertools/jmespath';
16+
import type { BasePersistenceLayer } from './persistence/BasePersistenceLayer.js';
17+
import type { IdempotencyRecord } from './persistence/IdempotencyRecord.js';
18+
import type {
19+
AnyFunction,
20+
IdempotencyHandlerOptions,
21+
} from './types/IdempotencyOptions.js';
2222

2323
/**
2424
* @internal
@@ -99,9 +99,8 @@ export class IdempotencyHandler<Func extends AnyFunction> {
9999
throw new IdempotencyInconsistentStateError(
100100
'Item has expired during processing and may not longer be valid.'
101101
);
102-
} else if (
103-
idempotencyRecord.getStatus() === IdempotencyRecordStatus.INPROGRESS
104-
) {
102+
}
103+
if (idempotencyRecord.getStatus() === IdempotencyRecordStatus.INPROGRESS) {
105104
if (
106105
idempotencyRecord.inProgressExpiryTimestamp &&
107106
idempotencyRecord.inProgressExpiryTimestamp <
@@ -110,11 +109,10 @@ export class IdempotencyHandler<Func extends AnyFunction> {
110109
throw new IdempotencyInconsistentStateError(
111110
'Item is in progress but the in progress expiry timestamp has expired.'
112111
);
113-
} else {
114-
throw new IdempotencyAlreadyInProgressError(
115-
`There is already an execution in progress with idempotency key: ${idempotencyRecord.idempotencyKey}`
116-
);
117112
}
113+
throw new IdempotencyAlreadyInProgressError(
114+
`There is already an execution in progress with idempotency key: ${idempotencyRecord.idempotencyKey}`
115+
);
118116
}
119117

120118
return idempotencyRecord.getResponse();
@@ -129,7 +127,7 @@ export class IdempotencyHandler<Func extends AnyFunction> {
129127
* @returns The result of the function execution
130128
*/
131129
public async getFunctionResult(): Promise<ReturnType<Func>> {
132-
let result;
130+
let result: ReturnType<Func>;
133131
try {
134132
result = await this.#functionToMakeIdempotent.apply(
135133
this.#thisArg,
@@ -168,7 +166,7 @@ export class IdempotencyHandler<Func extends AnyFunction> {
168166
);
169167
}
170168

171-
let e;
169+
let e: Error | undefined;
172170
for (let retryNo = 0; retryNo <= MAX_RETRIES; retryNo++) {
173171
try {
174172
const { isIdempotent, result } =
@@ -234,7 +232,7 @@ export class IdempotencyHandler<Func extends AnyFunction> {
234232
public async handleMiddyBefore(
235233
request: MiddyLikeRequest,
236234
callback: (request: MiddyLikeRequest) => Promise<void>
237-
): Promise<ReturnType<Func> | void> {
235+
): Promise<ReturnType<Func> | undefined> {
238236
for (let retryNo = 0; retryNo <= MAX_RETRIES; retryNo++) {
239237
try {
240238
const { isIdempotent, result } =
@@ -309,9 +307,9 @@ export class IdempotencyHandler<Func extends AnyFunction> {
309307
);
310308

311309
return selection === undefined || selection === null;
312-
} else {
313-
return false;
314310
}
311+
312+
return false;
315313
}
316314

317315
/**
@@ -388,12 +386,11 @@ export class IdempotencyHandler<Func extends AnyFunction> {
388386
);
389387

390388
return returnValue;
391-
} else {
392-
throw new IdempotencyPersistenceLayerError(
393-
'Failed to save in progress record to idempotency store',
394-
{ cause: error }
395-
);
396389
}
390+
throw new IdempotencyPersistenceLayerError(
391+
'Failed to save in progress record to idempotency store',
392+
{ cause: error }
393+
);
397394
}
398395
};
399396

packages/idempotency/src/deepSort.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { getType } from '@aws-lambda-powertools/commons';
2-
import {
2+
import type {
33
JSONArray,
44
JSONObject,
55
JSONValue,
@@ -36,7 +36,8 @@ const deepSort = (data: JSONValue): JSONValue => {
3636
const type = getType(data);
3737
if (type === 'object') {
3838
return sortObject(data as JSONObject);
39-
} else if (type === 'array') {
39+
}
40+
if (type === 'array') {
4041
return (data as JSONArray).map(deepSort);
4142
}
4243

packages/idempotency/src/idempotencyDecorator.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import type { Handler } from 'aws-lambda';
2-
import {
2+
import { makeIdempotent } from './makeIdempotent.js';
3+
import type {
34
AnyFunction,
45
ItempotentFunctionOptions,
56
} from './types/IdempotencyOptions.js';
6-
import { makeIdempotent } from './makeIdempotent.js';
77

88
/**
99
* Use this decorator to make your lambda handler itempotent.
@@ -60,6 +60,7 @@ const idempotent = function (
6060
propertyKey: string,
6161
descriptor: PropertyDescriptor
6262
) => PropertyDescriptor {
63+
// biome-ignore lint/complexity/useArrowFunction: this is a decorator function and we need to maintain the `this` context
6364
return function (
6465
_target: unknown,
6566
_propertyKey: string,

packages/idempotency/src/makeIdempotent.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1+
import type { JSONValue } from '@aws-lambda-powertools/commons/types';
12
import type { Context, Handler } from 'aws-lambda';
3+
import { IdempotencyConfig } from './IdempotencyConfig.js';
4+
import { IdempotencyHandler } from './IdempotencyHandler.js';
25
import type {
36
AnyFunction,
4-
ItempotentFunctionOptions,
57
IdempotencyLambdaHandlerOptions,
8+
ItempotentFunctionOptions,
69
} from './types/IdempotencyOptions.js';
7-
import { IdempotencyHandler } from './IdempotencyHandler.js';
8-
import { IdempotencyConfig } from './IdempotencyConfig.js';
910

1011
const isContext = (arg: unknown): arg is Context => {
1112
return (
@@ -72,7 +73,6 @@ const isOptionsWithDataIndexArgument = (
7273
*
7374
* ```
7475
*/
75-
// eslint-disable-next-line func-style
7676
function makeIdempotent<Func extends AnyFunction>(
7777
fn: Func,
7878
options: ItempotentFunctionOptions<Parameters<Func>>
@@ -83,7 +83,7 @@ function makeIdempotent<Func extends AnyFunction>(
8383
if (!idempotencyConfig.isEnabled()) return fn;
8484

8585
return function (this: Handler, ...args: Parameters<Func>): ReturnType<Func> {
86-
let functionPayloadToBeHashed;
86+
let functionPayloadToBeHashed: JSONValue;
8787

8888
if (isFnHandler(fn, args)) {
8989
idempotencyConfig.registerLambdaContext(args[1]);

packages/idempotency/src/persistence/BasePersistenceLayer.ts

+32-28
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
1-
import { createHash, Hash } from 'node:crypto';
1+
import { type Hash, createHash } from 'node:crypto';
2+
import type { JSONValue } from '@aws-lambda-powertools/commons/types';
23
import { search } from '@aws-lambda-powertools/jmespath';
34
import type { JMESPathParsingOptions } from '@aws-lambda-powertools/jmespath/types';
4-
import type {
5-
BasePersistenceLayerOptions,
6-
BasePersistenceLayerInterface,
7-
} from '../types/BasePersistenceLayer.js';
8-
import { IdempotencyRecordStatus } from '../constants.js';
95
import { EnvironmentVariablesService } from '../config/EnvironmentVariablesService.js';
10-
import { IdempotencyRecord } from './IdempotencyRecord.js';
6+
import { IdempotencyRecordStatus } from '../constants.js';
7+
import { deepSort } from '../deepSort.js';
118
import {
129
IdempotencyItemAlreadyExistsError,
1310
IdempotencyKeyError,
1411
IdempotencyValidationError,
1512
} from '../errors.js';
13+
import type {
14+
BasePersistenceLayerInterface,
15+
BasePersistenceLayerOptions,
16+
} from '../types/BasePersistenceLayer.js';
17+
import { IdempotencyRecord } from './IdempotencyRecord.js';
1618
import { LRUCache } from './LRUCache.js';
17-
import type { JSONValue } from '@aws-lambda-powertools/commons/types';
18-
import { deepSort } from '../deepSort.js';
1919

2020
/**
2121
* Base class for all persistence layers. This class provides the basic functionality for
@@ -282,15 +282,15 @@ abstract class BasePersistenceLayer implements BasePersistenceLayerInterface {
282282
* @returns the idempotency key
283283
*/
284284
private getHashedIdempotencyKey(data: JSONValue): string {
285-
if (this.eventKeyJmesPath) {
286-
data = search(
287-
this.eventKeyJmesPath,
288-
data,
289-
this.#jmesPathOptions
290-
) as JSONValue;
291-
}
292-
293-
if (BasePersistenceLayer.isMissingIdempotencyKey(data)) {
285+
const payload = this.eventKeyJmesPath
286+
? (search(
287+
this.eventKeyJmesPath,
288+
data,
289+
this.#jmesPathOptions
290+
) as JSONValue)
291+
: data;
292+
293+
if (BasePersistenceLayer.isMissingIdempotencyKey(payload)) {
294294
if (this.throwOnNoIdempotencyKey) {
295295
throw new IdempotencyKeyError(
296296
'No data found to create a hashed idempotency_key'
@@ -302,7 +302,7 @@ abstract class BasePersistenceLayer implements BasePersistenceLayerInterface {
302302
}
303303

304304
return `${this.idempotencyKeyPrefix}#${this.generateHash(
305-
JSON.stringify(deepSort(data))
305+
JSON.stringify(deepSort(payload))
306306
)}`;
307307
}
308308

@@ -313,16 +313,20 @@ abstract class BasePersistenceLayer implements BasePersistenceLayerInterface {
313313
*/
314314
private getHashedPayload(data: JSONValue): string {
315315
if (this.isPayloadValidationEnabled() && this.validationKeyJmesPath) {
316-
data = search(
317-
this.validationKeyJmesPath,
318-
data,
319-
this.#jmesPathOptions
320-
) as JSONValue;
321-
322-
return this.generateHash(JSON.stringify(deepSort(data)));
323-
} else {
324-
return '';
316+
return this.generateHash(
317+
JSON.stringify(
318+
deepSort(
319+
search(
320+
this.validationKeyJmesPath,
321+
data,
322+
this.#jmesPathOptions
323+
) as JSONValue
324+
)
325+
)
326+
);
325327
}
328+
329+
return '';
326330
}
327331

328332
private static isMissingIdempotencyKey(data: JSONValue): boolean {

packages/idempotency/src/persistence/DynamoDBPersistenceLayer.ts

+12-12
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,26 @@
11
import {
2-
IdempotencyItemAlreadyExistsError,
3-
IdempotencyItemNotFoundError,
4-
} from '../errors.js';
5-
import { IdempotencyRecordStatus } from '../constants.js';
6-
import type { DynamoDBPersistenceOptions } from '../types/DynamoDBPersistence.js';
2+
addUserAgentMiddleware,
3+
isSdkClient,
4+
} from '@aws-lambda-powertools/commons';
75
import {
8-
AttributeValue,
6+
type AttributeValue,
97
ConditionalCheckFailedException,
108
DeleteItemCommand,
119
DynamoDBClient,
12-
DynamoDBClientConfig,
10+
type DynamoDBClientConfig,
1311
GetItemCommand,
1412
PutItemCommand,
1513
UpdateItemCommand,
1614
} from '@aws-sdk/client-dynamodb';
1715
import { marshall, unmarshall } from '@aws-sdk/util-dynamodb';
18-
import { IdempotencyRecord } from './IdempotencyRecord.js';
19-
import { BasePersistenceLayer } from './BasePersistenceLayer.js';
16+
import { IdempotencyRecordStatus } from '../constants.js';
2017
import {
21-
addUserAgentMiddleware,
22-
isSdkClient,
23-
} from '@aws-lambda-powertools/commons';
18+
IdempotencyItemAlreadyExistsError,
19+
IdempotencyItemNotFoundError,
20+
} from '../errors.js';
21+
import type { DynamoDBPersistenceOptions } from '../types/DynamoDBPersistence.js';
22+
import { BasePersistenceLayer } from './BasePersistenceLayer.js';
23+
import { IdempotencyRecord } from './IdempotencyRecord.js';
2424

2525
/**
2626
* DynamoDB persistence layer for idempotency records.

packages/idempotency/src/persistence/IdempotencyRecord.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import type { JSONValue } from '@aws-lambda-powertools/commons/types';
2+
import { IdempotencyRecordStatus } from '../constants.js';
3+
import { IdempotencyInvalidStatusError } from '../errors.js';
24
import type {
35
IdempotencyRecordOptions,
46
IdempotencyRecordStatusValue,
57
} from '../types/IdempotencyRecord.js';
6-
import { IdempotencyRecordStatus } from '../constants.js';
7-
import { IdempotencyInvalidStatusError } from '../errors.js';
88

99
/**
1010
* Class representing an idempotency record.
@@ -62,11 +62,11 @@ class IdempotencyRecord {
6262
public getStatus(): IdempotencyRecordStatusValue {
6363
if (this.isExpired()) {
6464
return IdempotencyRecordStatus.EXPIRED;
65-
} else if (Object.values(IdempotencyRecordStatus).includes(this.status)) {
65+
}
66+
if (Object.values(IdempotencyRecordStatus).includes(this.status)) {
6667
return this.status;
67-
} else {
68-
throw new IdempotencyInvalidStatusError(this.status);
6968
}
69+
throw new IdempotencyInvalidStatusError(this.status);
7070
}
7171

7272
/**

packages/idempotency/src/persistence/LRUCache.ts

+2-4
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,7 @@ class LRUCache<K, V> {
9797
public add(key: K, value: V): void {
9898
// If the key already exists, we just update the value and mark it as the most recently used
9999
if (this.map.has(key)) {
100-
// At this point, we know that the key exists in the map, so we can safely use the non-null
101-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
100+
// 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
102101
const item = this.map.get(key)!;
103102
item.value = value;
104103
this.trackItemUse(item);
@@ -192,8 +191,7 @@ class LRUCache<K, V> {
192191
* Removes the oldest item from the cache and unlinks it from the linked list.
193192
*/
194193
private shift(): void {
195-
// If this function is called, we know that the least recently used item exists
196-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
194+
// biome-ignore lint/style/noNonNullAssertion: If this function is called, we know that the least recently used item exists
197195
const item = this.leastRecentlyUsed!;
198196

199197
// If there's a newer item, make it the oldest

packages/idempotency/src/types/BasePersistenceLayer.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { IdempotencyRecord } from '../persistence/IdempotencyRecord.js';
2-
import { IdempotencyConfig } from '../IdempotencyConfig.js';
1+
import type { IdempotencyConfig } from '../IdempotencyConfig.js';
2+
import type { IdempotencyRecord } from '../persistence/IdempotencyRecord.js';
33

44
type BasePersistenceLayerOptions = {
55
config: IdempotencyConfig;

0 commit comments

Comments
 (0)