@aws-lambda-powertools/idempotency
Version:
The idempotency package for the Powertools for AWS Lambda (TypeScript) library. It provides options to make your Lambda functions idempotent and safe to retry.
124 lines (123 loc) • 4.77 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.makeIdempotent = makeIdempotent;
const IdempotencyConfig_js_1 = require("./IdempotencyConfig.js");
const IdempotencyHandler_js_1 = require("./IdempotencyHandler.js");
const isContext = (arg) => {
return (arg !== undefined &&
arg !== null &&
typeof arg === 'object' &&
'getRemainingTimeInMillis' in arg);
};
const isFnHandler = (fn, args) => {
// get arguments of function
return (fn !== undefined &&
fn !== null &&
typeof fn === 'function' &&
isContext(args[1]));
};
const isOptionsWithDataIndexArgument = (options) => {
return (options !== undefined &&
options !== null &&
typeof options === 'object' &&
'dataIndexArgument' in options);
};
/**
* Function wrapper to make any function idempotent.
*
* The `makeIdempotent` function is a higher-order function that takes another function and returns a new version of that function with idempotency behavior.
* This means that if the function is called multiple times with the same input, it will return the same result without re-executing the original function logic.
*
* By default, the entire first argument is hashed to create the idempotency key. You can customize this behavior:
* - Use {@link IdempotencyConfig.eventKeyJmesPath | `eventKeyJmesPath`} to hash only a subset of the payload
* - Use {@link ItempotentFunctionOptions.dataIndexArgument | `dataIndexArgument`} to hash a different function argument
*
*
* **Using a subset of the payload**
*
* @example
* ```typescript
* import { makeIdempotent, IdempotencyConfig } from '@aws-lambda-powertools/idempotency';
* import { DynamoDBPersistenceLayer } from '@aws-lambda-powertools/idempotency/dynamodb';
*
* const processRecord = (record: Record<string, unknown>): unknown => {
* // your processing logic
* return result;
* };
*
* const processIdempotently = makeIdempotent(processRecord, {
* persistenceStore: new DynamoDBPersistenceLayer({ tableName: 'idempotency-table' }),
* config: new IdempotencyConfig({
* eventKeyJmesPath: 'transactionId', // hash only this field as idempotency key
* }),
* });
*
* export const handler = async (event: { records: Record<string, unknown>[] }) => {
* for (const record of event.records) {
* // use the idempotent function
* const result = await processIdempotently(record);
* // ... do something with the result
* }
* };
*```
*
* **Using a different function argument (useful for multi-parameter functions)**
*
* @example
* ```typescript
* import { makeIdempotent } from '@aws-lambda-powertools/idempotency';
* import { DynamoDBPersistenceLayer } from '@aws-lambda-powertools/idempotency/dynamodb';
*
* const processRecord = (record: Record<string, unknown>, userId: string): unknown => {
* // your processing logic
* return result;
* };
*
* const processIdempotently = makeIdempotent(processRecord, {
* persistenceStore: new DynamoDBPersistenceLayer({ tableName: 'idempotency-table' }),
* dataIndexArgument: 1, // hash the userId (second argument) instead of first (record)
* });
*
* export const handler = async (event: { records: Record<string,unknown>[]; userId: string }) => {
* for (const record of event.records) {
* const userId = event.userId;
* // use the idempotent function
* const result = await processIdempotently(record, userId);
* // ... do something with the result
* }
* };
* ```
*
* @param fn - the function to make idempotent
* @param options - the options to configure the idempotency behavior
*/
function makeIdempotent(fn, options) {
const { persistenceStore, config, keyPrefix } = options;
const idempotencyConfig = config ? config : new IdempotencyConfig_js_1.IdempotencyConfig({});
if (!idempotencyConfig.isEnabled())
return fn;
return function (...args) {
let functionPayloadToBeHashed;
if (isFnHandler(fn, args)) {
idempotencyConfig.registerLambdaContext(args[1]);
functionPayloadToBeHashed = args[0];
}
else {
if (isOptionsWithDataIndexArgument(options)) {
functionPayloadToBeHashed = args[options.dataIndexArgument];
}
else {
functionPayloadToBeHashed = args[0];
}
}
return new IdempotencyHandler_js_1.IdempotencyHandler({
functionToMakeIdempotent: fn,
idempotencyConfig: idempotencyConfig,
persistenceStore: persistenceStore,
keyPrefix: keyPrefix,
functionArguments: args,
functionPayloadToBeHashed,
thisArg: this,
}).handle();
};
}
;