@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.
157 lines (156 loc) • 6.4 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.makeHandlerIdempotent = void 0;
const commons_1 = require("@aws-lambda-powertools/commons");
const IdempotencyConfig_js_1 = require("../IdempotencyConfig.js");
const IdempotencyHandler_js_1 = require("../IdempotencyHandler.js");
/**
* @internal
* Utility function to get the idempotency handler from the request internal storage
*
* @param request The Middy request object
* @returns The idempotency handler from the request internal
*/
const getIdempotencyHandlerFromRequestInternal = (request) => {
const idempotencyHandler = request.internal[`${commons_1.IDEMPOTENCY_KEY}.idempotencyHandler`];
return idempotencyHandler;
};
/**
* @internal
* Utility function to set the idempotency handler in the request internal storage
*
* @param request The Middy request object
* @param idempotencyHandler The idempotency handler to set in the request internal
*/
const setIdempotencyHandlerInRequestInternal = (request, idempotencyHandler) => {
request.internal[`${commons_1.IDEMPOTENCY_KEY}.idempotencyHandler`] =
idempotencyHandler;
};
/**
* @internal
* Utility function to set a flag in the request internal storage to skip the idempotency middleware
* This is used to skip the idempotency middleware when the idempotency key is not present in the payload
* or when idempotency is disabled
*
* @param request The Middy request object
*/
const setIdempotencySkipFlag = (request) => {
request.internal[`${commons_1.IDEMPOTENCY_KEY}.skip`] = true;
};
/**
* @internal
* Utility function to get the idempotency key from the request internal storage
* and determine if the request should skip the idempotency middleware
*
* @param request The Middy request object
* @returns Whether the idempotency middleware should be skipped
*/
const shouldSkipIdempotency = (request) => {
return request.internal[`${commons_1.IDEMPOTENCY_KEY}.skip`] === true;
};
/**
* A middy middleware to make your Lambda Handler idempotent.
*
* @example
* ```typescript
* import { makeHandlerIdempotent } from '@aws-lambda-powertools/idempotency/middleware';
* import { DynamoDBPersistenceLayer } from '@aws-lambda-powertools/idempotency/dynamodb';
* import middy from '@middy/core';
*
* const persistenceStore = new DynamoDBPersistenceLayer({
* tableName: 'idempotencyTable',
* });
*
* export const handler = middy(
* async (_event: unknown, _context: unknown): Promise<void> => {
* // your code goes here
* }
* ).use(makeHandlerIdempotent({ persistenceStore: dynamoDBPersistenceLayer }));
* ```
*
* For the middleware to work, your Lambda function handler must return a value different from `undefined`.
* This is a [known limitation of the early return feature in Middy.js](https://github.com/middyjs/middy/issues/1236).
*
* If your use case requires early returns, you can use the {@link index.makeIdempotent | makeIdempotent()} function wrapper instead.
*
* @param options - Options for the idempotency middleware
*/
const makeHandlerIdempotent = (options) => {
/**
* Function called before the handler is executed.
*
* Before the handler is executed, we insantiate the {@link IdempotencyHandler} and
* set it in the request internal storage. We then configure the persistence store
* and set the payload to be hashed and Lambda context in the idempotency config.
*
* If idempotency is enabled and the idempotency key is present in the payload,
* we then run the idempotency operations. These are handled in {@link IdempotencyHandler.handleMiddyBefore}.
*
* @param request - The Middy request object
*/
const before = (request) => {
const idempotencyConfig = options.config ?? new IdempotencyConfig_js_1.IdempotencyConfig({});
const persistenceStore = options.persistenceStore;
const keyPrefix = options.keyPrefix;
persistenceStore.configure({
config: idempotencyConfig,
keyPrefix: keyPrefix,
});
const idempotencyHandler = new IdempotencyHandler_js_1.IdempotencyHandler({
functionToMakeIdempotent: /* v8 ignore next -- @preserve */ () => ({}),
functionArguments: [],
idempotencyConfig,
persistenceStore,
keyPrefix,
functionPayloadToBeHashed: undefined,
});
setIdempotencyHandlerInRequestInternal(request, idempotencyHandler);
// set the payload to be hashed
idempotencyHandler.setFunctionPayloadToBeHashed(request.event);
// check if we should skip idempotency checks
if (idempotencyHandler.shouldSkipIdempotency()) {
// set the flag to skip checks in after and onError
setIdempotencySkipFlag(request);
return;
}
idempotencyConfig.registerLambdaContext(request.context);
return idempotencyHandler.handleMiddyBefore(request, commons_1.cleanupMiddlewares);
};
/**
* Function called after the handler has executed successfully.
*
* When the handler returns successfully, we need to update the record in the
* idempotency store to indicate that the execution has completed and
* store its result. This is handled in {@link IdempotencyHandler.handleMiddyAfter}.
*
* @param request - The Middy request object
*/
const after = async (request) => {
if (shouldSkipIdempotency(request)) {
return;
}
const idempotencyHandler = getIdempotencyHandlerFromRequestInternal(request);
await idempotencyHandler.handleMiddyAfter(request.response);
};
/**
* Function called when an error occurs in the handler.
*
* When an error is thrown in the handler, we need to delete the record from the
* idempotency store. This is handled in {@link IdempotencyHandler.handleMiddyOnError}.
*
* @param request - The Middy request object
*/
const onError = async (request) => {
if (shouldSkipIdempotency(request)) {
return;
}
const idempotencyHandler = getIdempotencyHandlerFromRequestInternal(request);
await idempotencyHandler?.handleMiddyOnError();
};
return {
before,
after,
onError,
};
};
exports.makeHandlerIdempotent = makeHandlerIdempotent;