UNPKG

@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
"use strict"; 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;