Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions packages/idempotency/src/IdempotencyHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -383,20 +383,20 @@ export class IdempotencyHandler<Func extends AnyFunction> {
if (error.name === 'IdempotencyItemAlreadyExistsError') {
let idempotencyRecord = (error as IdempotencyItemAlreadyExistsError)
.existingRecord;
if (idempotencyRecord !== undefined) {
// If the error includes the existing record, we can use it to validate
// the record being processed and cache it in memory.
idempotencyRecord = this.#persistenceStore.processExistingRecord(
idempotencyRecord,
this.#functionPayloadToBeHashed
);
if (idempotencyRecord === undefined) {
// If the error doesn't include the existing record, we need to fetch
// it from the persistence layer. In doing so, we also call the processExistingRecord
// method to validate the record and cache it in memory.
} else {
idempotencyRecord = await this.#persistenceStore.getRecord(
this.#functionPayloadToBeHashed
);
} else {
// If the error includes the existing record, we can use it to validate
// the record being processed and cache it in memory.
idempotencyRecord = this.#persistenceStore.processExistingRecord(
idempotencyRecord,
this.#functionPayloadToBeHashed
);
}

returnValue.isIdempotent = true;
Expand Down
2 changes: 1 addition & 1 deletion packages/idempotency/src/idempotencyDecorator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ const idempotent = (
) {
const childFunction = descriptor.value;

descriptor.value = async function (this: Handler, ...args: unknown[]) {
descriptor.value = function (this: Handler, ...args: unknown[]) {
return makeIdempotent(childFunction, options).bind(this)(...args);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ const makeHandlerIdempotent = (
*
* @param request - The Middy request object
*/
const before = async (request: MiddyLikeRequest): Promise<unknown> => {
const before = (request: MiddyLikeRequest): unknown => {
const idempotencyConfig = options.config
? options.config
: new IdempotencyConfig({});
Expand Down
12 changes: 6 additions & 6 deletions packages/idempotency/src/persistence/DynamoDBPersistenceLayer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,13 @@ class DynamoDBPersistenceLayer extends BasePersistenceLayer {
config.staticPkValue ?? `idempotency#${this.idempotencyKeyPrefix}`;

if (config.awsSdkV3Client) {
if (!isSdkClient(config.awsSdkV3Client)) {
if (isSdkClient(config.awsSdkV3Client)) {
this.client = config.awsSdkV3Client;
} else {
console.warn(
'awsSdkV3Client is not an AWS SDK v3 client, using default client'
);
this.client = new DynamoDBClient(config.clientConfig ?? {});
} else {
this.client = config.awsSdkV3Client;
}
} else {
this.client = new DynamoDBClient(config.clientConfig ?? {});
Expand Down Expand Up @@ -168,9 +168,9 @@ class DynamoDBPersistenceLayer extends BasePersistenceLayer {
* | (in_progress_expiry) (expiry)
*
* Conditions to successfully save a record:
* * The idempotency key does not exist:
* - first time that this invocation key is used
* - previous invocation with the same key was deleted due to TTL
* - The idempotency key does not exist:
* - first time that this invocation key is used
* - previous invocation with the same key was deleted due to TTL
*/
const idempotencyKeyDoesNotExist = 'attribute_not_exists(#id)';
// * The idempotency key exists but it is expired
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { LambdaInterface } from '@aws-lambda-powertools/commons/types';
import { Logger } from '@aws-lambda-powertools/logger';
import type { Context } from 'aws-lambda';
import { IdempotencyConfig } from '../../src/IdempotencyConfig.js';
import { idempotent } from '../../src/idempotencyDecorator';
import { idempotent } from '../../src/idempotencyDecorator.js';
import { DynamoDBPersistenceLayer } from '../../src/persistence/DynamoDBPersistenceLayer.js';

const IDEMPOTENCY_TABLE_NAME =
Expand Down Expand Up @@ -35,19 +35,13 @@ class DefaultLambda implements LambdaInterface {
logger.info(`${this.message} ${JSON.stringify(_event)}`);
// sleep to enforce error with parallel execution
await new Promise((resolve) => setTimeout(resolve, 1000));

// We return void to test that the utility handles it correctly
return;
}

@idempotent({
persistenceStore: dynamoDBPersistenceLayerCustomized,
config: config,
})
public async handlerCustomized(
event: { foo: string },
context: Context
): Promise<string> {
public handlerCustomized(event: { foo: string }, context: Context) {
config.registerLambdaContext(context);
logger.info('Processed event', { details: event.foo });

Expand All @@ -62,10 +56,10 @@ class DefaultLambda implements LambdaInterface {
eventKeyJmesPath: 'foo',
}),
})
public async handlerExpired(
public handlerExpired(
event: { foo: string; invocation: number },
context: Context
): Promise<{ foo: string; invocation: number }> {
) {
logger.addContext(context);

logger.info('Processed event', { details: event.foo });
Expand All @@ -77,10 +71,7 @@ class DefaultLambda implements LambdaInterface {
}

@idempotent({ persistenceStore: dynamoDBPersistenceLayer })
public async handlerParallel(
event: { foo: string },
context: Context
): Promise<string> {
public async handlerParallel(event: { foo: string }, context: Context) {
logger.addContext(context);

await new Promise((resolve) => setTimeout(resolve, 1500));
Expand All @@ -99,7 +90,7 @@ class DefaultLambda implements LambdaInterface {
public async handlerTimeout(
event: { foo: string; invocation: number },
context: Context
): Promise<{ foo: string; invocation: number }> {
) {
logger.addContext(context);

if (event.invocation === 0) {
Expand Down Expand Up @@ -130,12 +121,9 @@ const handlerExpired = defaultLambda.handlerExpired.bind(defaultLambda);
const logger = new Logger();

class LambdaWithKeywordArgument implements LambdaInterface {
public async handler(
event: { id: string },
_context: Context
): Promise<string> {
public handler(event: { id: string }, _context: Context) {
config.registerLambdaContext(_context);
await this.process(event.id, 'bar');
this.process(event.id, 'bar');

return 'Hello World Keyword Argument';
}
Expand All @@ -145,7 +133,7 @@ class LambdaWithKeywordArgument implements LambdaInterface {
config: config,
dataIndexArgument: 1,
})
public async process(id: string, foo: string): Promise<string> {
public process(id: string, foo: string) {
logger.info('Got test event', { id, foo });

return `idempotent result: ${foo}`;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,12 @@ const logger = new Logger();
/**
* Test handler with sequential execution.
*/
export const handler = middy(
async (event: { foo: string }, context: Context) => {
logger.addContext(context);
logger.info('foo', { details: event.foo });
export const handler = middy((event: { foo: string }, context: Context) => {
logger.addContext(context);
logger.info('foo', { details: event.foo });

return event.foo;
}
).use(
return event.foo;
}).use(
makeHandlerIdempotent({
persistenceStore: dynamoDBPersistenceLayer,
})
Expand Down Expand Up @@ -97,7 +95,7 @@ export const handlerTimeout = middy(
* was processed by looking at the value in the stored idempotency record.
*/
export const handlerExpired = middy(
async (event: { foo: string; invocation: number }, context: Context) => {
(event: { foo: string; invocation: number }, context: Context) => {
logger.addContext(context);

logger.info('Processed event', { details: event.foo });
Expand Down
14 changes: 6 additions & 8 deletions packages/idempotency/tests/e2e/makeHandlerIdempotent.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,15 +192,13 @@ describe('Idempotency E2E tests, middy middleware usage', () => {
* We filter the logs to find which one was successful and which one failed, then we check
* that they contain the expected logs.
*/
const successfulInvocationLogs = functionLogs.find(
(functionLog) =>
functionLog.find((log) => log.includes('Processed event')) !== undefined
const successfulInvocationLogs = functionLogs.find((functionLog) =>
functionLog.some((log) => log.includes('Processed event'))
);
const failedInvocationLogs = functionLogs.find(
(functionLog) =>
functionLog.find((log) =>
log.includes('There is already an execution in progress')
) !== undefined
const failedInvocationLogs = functionLogs.find((functionLog) =>
functionLog.some((log) =>
log.includes('There is already an execution in progress')
)
);
expect(successfulInvocationLogs).toHaveLength(1);
expect(failedInvocationLogs).toHaveLength(1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export const handlerCustomized = async (
* Test idempotent Lambda handler with JMESPath expression to extract event key.
*/
export const handlerLambda = makeIdempotent(
async (event: { body: string }, context: Context) => {
(event: { body: string }, context: Context) => {
logger.addContext(context);
const body = JSON.parse(event.body);
logger.info('foo', { details: body.foo });
Expand Down
2 changes: 1 addition & 1 deletion packages/idempotency/tests/unit/IdempotencyConfig.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ describe('Class: IdempotencyConfig', () => {
});

describe('Method: registerLambdaContext', () => {
it('stores the provided context', async () => {
it('stores the provided context', () => {
// Prepare
const config = new IdempotencyConfig({});

Expand Down
6 changes: 3 additions & 3 deletions packages/idempotency/tests/unit/IdempotencyHandler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ describe('Class IdempotencyHandler', () => {
},
])(
'throws when the record is in progress and within expiry window ($case)',
async ({ keys, expectedErrorMsg }) => {
({ keys, expectedErrorMsg }) => {
// Prepare
const stubRecord = new IdempotencyRecord({
...keys,
Expand All @@ -89,7 +89,7 @@ describe('Class IdempotencyHandler', () => {
}
);

it('throws when the record is in progress and outside expiry window', async () => {
it('throws when the record is in progress and outside expiry window', () => {
// Prepare
const stubRecord = new IdempotencyRecord({
idempotencyKey: 'idempotencyKey',
Expand All @@ -109,7 +109,7 @@ describe('Class IdempotencyHandler', () => {
expect(mockResponseHook).not.toHaveBeenCalled();
});

it('throws when the idempotency record is expired', async () => {
it('throws when the idempotency record is expired', () => {
// Prepare
const stubRecord = new IdempotencyRecord({
idempotencyKey: 'idempotencyKey',
Expand Down
2 changes: 1 addition & 1 deletion packages/idempotency/tests/unit/deepSort.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, expect, it } from 'vitest';
import { deepSort } from '../../src/deepSort';
import { deepSort } from '../../src/deepSort.js';

describe('Function: deepSort', () => {
it('can sort string correctly', () => {
Expand Down
10 changes: 2 additions & 8 deletions packages/idempotency/tests/unit/idempotencyDecorator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,7 @@ describe('Given a class with a function to decorate', () => {
@idempotent({
persistenceStore: new PersistenceLayerTestClass(),
})
public async handler(
_event: unknown,
_context: Context
): Promise<string> {
public handler(_event: unknown, _context: Context) {
return this.privateMethod();
}

Expand Down Expand Up @@ -48,10 +45,7 @@ describe('Given a class with a function to decorate', () => {
config: idempotencyConfig,
keyPrefix: 'my-custom-prefix',
})
public async handler(
_event: unknown,
_context: Context
): Promise<boolean> {
public handler(_event: unknown, _context: Context) {
return true;
}
}
Expand Down
4 changes: 2 additions & 2 deletions packages/idempotency/tests/unit/makeIdempotent.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const mockIdempotencyOptions = {
};
const remainingTImeInMillis = 1234;
const fnSuccessfull = async () => true;
const fnError = async () => {
const fnError = () => {
throw new Error('Something went wrong');
};

Expand Down Expand Up @@ -525,7 +525,7 @@ describe('Function: makeIdempotent', () => {

it('throws immediately if an object other than an error was thrown (wrapper)', async () => {
// Prepare
const fn = async (_event: unknown, _context: Context) => {
const fn = (_event: unknown, _context: Context) => {
throw 'a string';
};
const handler = makeIdempotent(fn, mockIdempotencyOptions);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ describe('Class: DynamoDBPersistenceLayer', () => {
);
});

it('falls back on a new SDK client and logs a warning when an unknown object is provided instead of a client', async () => {
it('falls back on a new SDK client and logs a warning when an unknown object is provided instead of a client', () => {
// Act
const persistenceLayer = new DynamoDBPersistenceLayerTestClass({
tableName: dummyTableName,
Expand Down Expand Up @@ -424,7 +424,7 @@ describe('Class: DynamoDBPersistenceLayer', () => {
expiryTimestamp: 0,
inProgressExpiryTimestamp: 0,
});
client.on(PutItemCommand).rejects(new Error());
client.on(PutItemCommand).rejects(new Error('some error'));

// Act & Assess
await expect(persistenceLayer._putRecord(record)).rejects.toThrow();
Expand Down Expand Up @@ -576,7 +576,7 @@ describe('Class: DynamoDBPersistenceLayer', () => {
});
});

it('updates the item when the response_data is undefined', async () => {
it('updates the item when the response_data is undefined', () => {
// Prepare
const status = IdempotencyRecordStatus.EXPIRED;
const expiryTimestamp = Date.now();
Expand Down
4 changes: 2 additions & 2 deletions packages/idempotency/vitest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export default defineProject({
test: {
environment: 'node',
setupFiles: ['../testing/src/setupEnv.ts'],
hookTimeout: 1_000 * 60 * 10, // 10 minutes
testTimeout: 1_000 * 60 * 3, // 3 minutes
hookTimeout: 1000 * 60 * 10, // 10 minutes
testTimeout: 1000 * 60 * 3, // 3 minutes
},
});
Loading