Skip to content

Commit b9fcef6

Browse files
author
Alexander Schueren
authoredJun 20, 2023
fix(idempotency): skip persistence for optional idempotency key (#1507)
* add skip idempotency step in the handler in specific cases
1 parent 043b4ad commit b9fcef6

17 files changed

+311
-109
lines changed
 

‎package.json

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"scripts": {
1818
"init-environment": "husky install",
1919
"test": "npm t -ws",
20+
"test:e2e": "npm run test:e2e -ws",
2021
"commit": "commit",
2122
"package": "npm run package -ws",
2223
"setup-local": "npm ci && npm run build && npm run init-environment",

‎packages/commons/tests/utils/e2eUtils.ts

+3-9
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* E2E utils is used by e2e tests. They are helper function that calls either CDK or SDK
33
* to interact with services.
44
*/
5-
import { App, CfnOutput, Stack, Duration } from 'aws-cdk-lib';
5+
import { App, CfnOutput, Duration, Stack } from 'aws-cdk-lib';
66
import {
77
NodejsFunction,
88
NodejsFunctionProps,
@@ -91,15 +91,11 @@ export const invokeFunction = async (
9191
): Promise<InvocationLogs[]> => {
9292
const invocationLogs: InvocationLogs[] = [];
9393

94-
const promiseFactory = (
95-
index?: number,
96-
includeIndex = true
97-
): Promise<void> => {
94+
const promiseFactory = (index?: number): Promise<void> => {
9895
// in some cases we need to send a payload without the index, i.e. idempotency tests
9996
const payloadToSend = includeIndex
10097
? { invocation: index, ...payload }
10198
: { ...payload };
102-
10399
const invokePromise = lambdaClient
104100
.send(
105101
new InvokeCommand({
@@ -126,9 +122,7 @@ export const invokeFunction = async (
126122

127123
const invocation =
128124
invocationMode == 'PARALLEL'
129-
? Promise.all(
130-
promiseFactories.map((factory, index) => factory(index, includeIndex))
131-
)
125+
? Promise.all(promiseFactories.map((factory, index) => factory(index)))
132126
: chainPromises(promiseFactories);
133127
await invocation;
134128

‎packages/idempotency/src/IdempotencyConfig.ts

+2
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ class IdempotencyConfig {
3838
/**
3939
* Throw an error if the idempotency key is not found in the event.
4040
* In some cases, you may want to allow the request to continue without idempotency.
41+
* If set to false and idempotency key is not found, the request will continue without idempotency.
42+
* @default false
4143
*/
4244
public throwOnNoIdempotencyKey: boolean;
4345
/**

‎packages/idempotency/src/IdempotencyHandler.ts

+31
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
import { BasePersistenceLayer, IdempotencyRecord } from './persistence';
1010
import { IdempotencyConfig } from './IdempotencyConfig';
1111
import { MAX_RETRIES } from './constants';
12+
import { search } from 'jmespath';
1213

1314
/**
1415
* @internal
@@ -127,6 +128,17 @@ export class IdempotencyHandler<U> {
127128
}
128129

129130
public async processIdempotency(): Promise<U> {
131+
// early return if we should skip idempotency completely
132+
if (
133+
IdempotencyHandler.shouldSkipIdempotency(
134+
this.idempotencyConfig.eventKeyJmesPath,
135+
this.idempotencyConfig.throwOnNoIdempotencyKey,
136+
this.fullFunctionPayload
137+
)
138+
) {
139+
return await this.functionToMakeIdempotent(this.fullFunctionPayload);
140+
}
141+
130142
try {
131143
await this.persistenceStore.saveInProgress(
132144
this.functionPayloadToBeHashed
@@ -146,4 +158,23 @@ export class IdempotencyHandler<U> {
146158

147159
return this.getFunctionResult();
148160
}
161+
162+
/**
163+
* avoid idempotency if the eventKeyJmesPath is not present in the payload and throwOnNoIdempotencyKey is false
164+
* static so {@link makeHandlerIdempotent} middleware can use it
165+
* TOOD: refactor so middy uses IdempotencyHandler internally wihtout reimplementing the logic
166+
* @param eventKeyJmesPath
167+
* @param throwOnNoIdempotencyKey
168+
* @param fullFunctionPayload
169+
* @private
170+
*/
171+
public static shouldSkipIdempotency(
172+
eventKeyJmesPath: string,
173+
throwOnNoIdempotencyKey: boolean,
174+
fullFunctionPayload: Record<string, unknown>
175+
): boolean {
176+
return (eventKeyJmesPath &&
177+
!throwOnNoIdempotencyKey &&
178+
!search(fullFunctionPayload, eventKeyJmesPath)) as boolean;
179+
}
149180
}

‎packages/idempotency/src/middleware/makeHandlerIdempotent.ts

+22-2
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import { IdempotencyHandler } from '../IdempotencyHandler';
22
import { IdempotencyConfig } from '../IdempotencyConfig';
33
import { cleanupMiddlewares } from '@aws-lambda-powertools/commons/lib/middleware';
44
import {
5+
IdempotencyInconsistentStateError,
56
IdempotencyItemAlreadyExistsError,
67
IdempotencyPersistenceLayerError,
7-
IdempotencyInconsistentStateError,
88
} from '../Exceptions';
99
import { IdempotencyRecord } from '../persistence';
1010
import { MAX_RETRIES } from '../constants';
@@ -50,6 +50,9 @@ const makeHandlerIdempotent = (
5050
config: idempotencyConfig,
5151
});
5252

53+
// keep the flag for after and onError checks
54+
let shouldSkipIdempotency = false;
55+
5356
/**
5457
* Function called before the handler is executed.
5558
*
@@ -72,6 +75,18 @@ const makeHandlerIdempotent = (
7275
request: MiddyLikeRequest,
7376
retryNo = 0
7477
): Promise<unknown | void> => {
78+
if (
79+
IdempotencyHandler.shouldSkipIdempotency(
80+
idempotencyConfig.eventKeyJmesPath,
81+
idempotencyConfig.throwOnNoIdempotencyKey,
82+
request.event as Record<string, unknown>
83+
)
84+
) {
85+
// set the flag to skip checks in after and onError
86+
shouldSkipIdempotency = true;
87+
88+
return;
89+
}
7590
try {
7691
await persistenceStore.saveInProgress(
7792
request.event as Record<string, unknown>,
@@ -114,7 +129,6 @@ const makeHandlerIdempotent = (
114129
}
115130
}
116131
};
117-
118132
/**
119133
* Function called after the handler has executed successfully.
120134
*
@@ -125,6 +139,9 @@ const makeHandlerIdempotent = (
125139
* @param request - The Middy request object
126140
*/
127141
const after = async (request: MiddyLikeRequest): Promise<void> => {
142+
if (shouldSkipIdempotency) {
143+
return;
144+
}
128145
try {
129146
await persistenceStore.saveSuccess(
130147
request.event as Record<string, unknown>,
@@ -146,6 +163,9 @@ const makeHandlerIdempotent = (
146163
* @param request - The Middy request object
147164
*/
148165
const onError = async (request: MiddyLikeRequest): Promise<void> => {
166+
if (shouldSkipIdempotency) {
167+
return;
168+
}
149169
try {
150170
await persistenceStore.deleteRecord(
151171
request.event as Record<string, unknown>

‎packages/idempotency/src/persistence/BasePersistenceLayer.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { createHash, Hash } from 'node:crypto';
22
import { search } from 'jmespath';
3-
import { IdempotencyRecordStatus } from '../types';
43
import type { BasePersistenceLayerOptions } from '../types';
4+
import { IdempotencyRecordStatus } from '../types';
55
import { EnvironmentVariablesService } from '../config';
66
import { IdempotencyRecord } from './IdempotencyRecord';
77
import { BasePersistenceLayerInterface } from './BasePersistenceLayerInterface';
@@ -176,10 +176,13 @@ abstract class BasePersistenceLayer implements BasePersistenceLayerInterface {
176176
}
177177

178178
protected abstract _deleteRecord(record: IdempotencyRecord): Promise<void>;
179+
179180
protected abstract _getRecord(
180181
idempotencyKey: string
181182
): Promise<IdempotencyRecord>;
183+
182184
protected abstract _putRecord(record: IdempotencyRecord): Promise<void>;
185+
183186
protected abstract _updateRecord(record: IdempotencyRecord): Promise<void>;
184187

185188
private deleteFromCache(idempotencyKey: string): void {
@@ -294,7 +297,7 @@ abstract class BasePersistenceLayer implements BasePersistenceLayerInterface {
294297
* Save record to local cache except for when status is `INPROGRESS`.
295298
*
296299
* We can't cache `INPROGRESS` records because we have no way to reflect updates
297-
* that might happen to the record outside of the execution context of the function.
300+
* that might happen to the record outside the execution context of the function.
298301
*
299302
* @param record - record to save
300303
*/

‎packages/idempotency/tests/e2e/idempotencyDecorator.test.FunctionCode.ts

+20-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { LambdaInterface } from '@aws-lambda-powertools/commons';
33
import { idempotentFunction, idempotentLambdaHandler } from '../../src';
44
import { Logger } from '../../../logger';
55
import { DynamoDBPersistenceLayer } from '../../src/persistence/DynamoDBPersistenceLayer';
6+
import { IdempotencyConfig } from '../../src/';
67

78
const IDEMPOTENCY_TABLE_NAME =
89
process.env.IDEMPOTENCY_TABLE_NAME || 'table_name';
@@ -62,10 +63,25 @@ class DefaultLambda implements LambdaInterface {
6263
_context: Context
6364
): Promise<string> {
6465
logger.info(`Got test event: ${JSON.stringify(_event)}`);
65-
// sleep for 5 seconds
6666

6767
throw new Error('Failed');
6868
}
69+
70+
@idempotentLambdaHandler({
71+
persistenceStore: dynamoDBPersistenceLayer,
72+
config: new IdempotencyConfig({
73+
eventKeyJmesPath: 'idempotencyKey',
74+
throwOnNoIdempotencyKey: false,
75+
}),
76+
})
77+
public async handlerWithOptionalIdempoitencyKey(
78+
_event: TestEvent,
79+
_context: Context
80+
): Promise<string> {
81+
logger.info(`Got test event: ${JSON.stringify(_event)}`);
82+
83+
return 'This should not be stored in DynamoDB';
84+
}
6985
}
7086

7187
const defaultLambda = new DefaultLambda();
@@ -74,6 +90,9 @@ export const handlerCustomized =
7490
defaultLambda.handlerCustomized.bind(defaultLambda);
7591
export const handlerFails = defaultLambda.handlerFails.bind(defaultLambda);
7692

93+
export const handlerWithOptionalIdempoitencyKey =
94+
defaultLambda.handlerWithOptionalIdempoitencyKey.bind(defaultLambda);
95+
7796
const logger = new Logger();
7897

7998
class LambdaWithKeywordArgument implements LambdaInterface {

‎packages/idempotency/tests/e2e/idempotencyDecorator.test.ts

+39-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import {
2222
destroyStack,
2323
} from '../../../commons/tests/utils/cdk-cli';
2424
import { LEVEL } from '../../../commons/tests/utils/InvocationLogs';
25-
import { GetCommand } from '@aws-sdk/lib-dynamodb';
25+
import { GetCommand, ScanCommand } from '@aws-sdk/lib-dynamodb';
2626
import { createHash } from 'node:crypto';
2727
import { createIdempotencyResources } from '../helpers/idempotencyUtils';
2828

@@ -110,6 +110,23 @@ createIdempotencyResources(
110110
functionNameFails,
111111
'handlerFails'
112112
);
113+
114+
const functionNameOptionalIdempotencyKey = generateUniqueName(
115+
RESOURCE_NAME_PREFIX,
116+
uuid,
117+
runtime,
118+
'optionalIdempotencyKey'
119+
);
120+
const ddbTableNameOptionalIdempotencyKey =
121+
stackName + '-optional-idempotencyKey-table';
122+
createIdempotencyResources(
123+
stack,
124+
runtime,
125+
ddbTableNameOptionalIdempotencyKey,
126+
decoratorFunctionFile,
127+
functionNameOptionalIdempotencyKey,
128+
'handlerWithOptionalIdempoitencyKey'
129+
);
113130
describe('Idempotency e2e test decorator, default settings', () => {
114131
beforeAll(async () => {
115132
await deployStack(app, stack);
@@ -285,6 +302,27 @@ describe('Idempotency e2e test decorator, default settings', () => {
285302
TEST_CASE_TIMEOUT
286303
);
287304

305+
test(
306+
'when called with a function with optional idempotency key and thorwOnNoIdempotencyKey is false, it does not create ddb entry',
307+
async () => {
308+
const payload = { foo: 'baz' }; // we set eventKeyJmesPath: 'idempotencyKey' in the idempotency configuration
309+
await invokeFunction(
310+
functionNameOptionalIdempotencyKey,
311+
2,
312+
'PARALLEL',
313+
payload,
314+
false
315+
);
316+
const result = await ddb.send(
317+
new ScanCommand({
318+
TableName: ddbTableNameOptionalIdempotencyKey,
319+
})
320+
);
321+
expect(result?.Items).toEqual([]);
322+
},
323+
TEST_CASE_TIMEOUT
324+
);
325+
288326
afterAll(async () => {
289327
if (!process.env.DISABLE_TEARDOWN) {
290328
await destroyStack(app, stack);

‎packages/idempotency/tests/unit/IdempotencyHandler.test.ts

+38
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,44 @@ describe('Class IdempotencyHandler', () => {
209209
expect(mockGetRecord).toHaveBeenCalledTimes(1);
210210
expect(mockDetermineResultFromIdempotencyRecord).toHaveBeenCalledTimes(1);
211211
});
212+
213+
test('when throwOnNoIdempotencyKey is false and the key is missing, we skip idempotency', async () => {
214+
const idempotentHandlerSkips = new IdempotencyHandler({
215+
functionToMakeIdempotent: mockFunctionToMakeIdempotent,
216+
functionPayloadToBeHashed: mockFunctionPayloadToBeHashed,
217+
persistenceStore: mockIdempotencyOptions.persistenceStore,
218+
fullFunctionPayload: mockFullFunctionPayload,
219+
idempotencyConfig: new IdempotencyConfig({
220+
throwOnNoIdempotencyKey: false,
221+
eventKeyJmesPath: 'idempotencyKey',
222+
}),
223+
});
224+
225+
const mockSaveInProgress = jest.spyOn(
226+
mockIdempotencyOptions.persistenceStore,
227+
'saveInProgress'
228+
);
229+
230+
const mockSaveSuccessfulResult = jest.spyOn(
231+
mockIdempotencyOptions.persistenceStore,
232+
'saveSuccess'
233+
);
234+
const mockGetRecord = jest.spyOn(
235+
mockIdempotencyOptions.persistenceStore,
236+
'getRecord'
237+
);
238+
239+
mockFunctionToMakeIdempotent.mockImplementation(() => {
240+
return 'result';
241+
});
242+
243+
await expect(idempotentHandlerSkips.processIdempotency()).resolves.toBe(
244+
'result'
245+
);
246+
expect(mockSaveInProgress).toHaveBeenCalledTimes(0);
247+
expect(mockGetRecord).toHaveBeenCalledTimes(0);
248+
expect(mockSaveSuccessfulResult).toHaveBeenCalledTimes(0);
249+
});
212250
});
213251

214252
describe('Method: getFunctionResult', () => {

‎packages/idempotency/tests/unit/makeHandlerIdempotent.test.ts

+58-2
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ import { Custom as dummyEvent } from '../../../commons/src/samples/resources/eve
99
import { IdempotencyRecordStatus } from '../../src/types';
1010
import { IdempotencyRecord } from '../../src/persistence';
1111
import {
12-
IdempotencyPersistenceLayerError,
13-
IdempotencyItemAlreadyExistsError,
1412
IdempotencyInconsistentStateError,
13+
IdempotencyItemAlreadyExistsError,
14+
IdempotencyPersistenceLayerError,
1515
} from '../../src/Exceptions';
1616
import { IdempotencyConfig } from '../../src/';
1717
import middy from '@middy/core';
@@ -268,4 +268,60 @@ describe('Middleware: makeHandlerIdempotent', () => {
268268
expect(saveInProgressSpy).toHaveBeenCalledTimes(0);
269269
expect(saveSuccessSpy).toHaveBeenCalledTimes(0);
270270
});
271+
272+
it('skips idempotency if no idempotency key is provided and throwOnNoIdempotencyKey is false', async () => {
273+
// Prepare
274+
const handler = middy(
275+
async (_event: unknown, _context: Context): Promise<boolean> => true
276+
).use(
277+
makeHandlerIdempotent({
278+
...mockIdempotencyOptions,
279+
config: new IdempotencyConfig({
280+
eventKeyJmesPath: 'idempotencyKey',
281+
throwOnNoIdempotencyKey: false,
282+
}),
283+
})
284+
);
285+
const saveInProgressSpy = jest.spyOn(
286+
mockIdempotencyOptions.persistenceStore,
287+
'saveInProgress'
288+
);
289+
const saveSuccessSpy = jest.spyOn(
290+
mockIdempotencyOptions.persistenceStore,
291+
'saveSuccess'
292+
);
293+
294+
// Act
295+
const result = await handler(event, context);
296+
297+
// Assess
298+
expect(result).toBe(true);
299+
expect(saveInProgressSpy).toHaveBeenCalledTimes(0);
300+
expect(saveSuccessSpy).toHaveBeenCalledTimes(0);
301+
});
302+
303+
it(' skips idempotency if error is thrown in the middleware', async () => {
304+
const handler = middy(
305+
async (_event: unknown, _context: Context): Promise<void> => {
306+
throw new Error('Something went wrong');
307+
}
308+
).use(
309+
makeHandlerIdempotent({
310+
...mockIdempotencyOptions,
311+
config: new IdempotencyConfig({
312+
eventKeyJmesPath: 'idempotencyKey',
313+
throwOnNoIdempotencyKey: false,
314+
}),
315+
})
316+
);
317+
318+
const deleteRecordSpy = jest.spyOn(
319+
mockIdempotencyOptions.persistenceStore,
320+
'deleteRecord'
321+
);
322+
323+
await expect(handler(event, context)).rejects.toThrowError();
324+
325+
expect(deleteRecordSpy).toHaveBeenCalledTimes(0);
326+
});
271327
});

‎packages/idempotency/tests/unit/persistence/BasePersistenceLayer.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
import { ContextExamples as dummyContext } from '@aws-lambda-powertools/commons';
77
import { IdempotencyConfig } from '../../../src';
88
import {
9-
IdempotencyRecord,
109
BasePersistenceLayer,
10+
IdempotencyRecord,
1111
} from '../../../src/persistence';
1212
import {
1313
IdempotencyItemAlreadyExistsError,

‎packages/logger/tests/e2e/basicFeatures.middy.test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ import {
2020
} from '../../../commons/tests/utils/cdk-cli';
2121
import {
2222
RESOURCE_NAME_PREFIX,
23-
STACK_OUTPUT_LOG_GROUP,
2423
SETUP_TIMEOUT,
25-
TEST_CASE_TIMEOUT,
24+
STACK_OUTPUT_LOG_GROUP,
2625
TEARDOWN_TIMEOUT,
26+
TEST_CASE_TIMEOUT,
2727
XRAY_TRACE_ID_REGEX,
2828
} from './constants';
2929

‎packages/tracer/tests/e2e/allFeatures.decorator.test.ts

+24-24
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
*/
66

77
import path from 'path';
8-
import { Table, AttributeType, BillingMode } from 'aws-cdk-lib/aws-dynamodb';
9-
import { App, Stack, RemovalPolicy } from 'aws-cdk-lib';
8+
import { AttributeType, BillingMode, Table } from 'aws-cdk-lib/aws-dynamodb';
9+
import { App, RemovalPolicy, Stack } from 'aws-cdk-lib';
1010
import { XRayClient } from '@aws-sdk/client-xray';
1111
import { STSClient } from '@aws-sdk/client-sts';
1212
import { v4 } from 'uuid';
@@ -15,29 +15,29 @@ import {
1515
destroyStack,
1616
} from '../../../commons/tests/utils/cdk-cli';
1717
import {
18-
getTraces,
19-
getInvocationSubsegment,
20-
splitSegmentsByName,
21-
invokeAllTestCases,
2218
createTracerTestFunction,
23-
getFunctionArn,
2419
getFirstSubsegment,
20+
getFunctionArn,
21+
getInvocationSubsegment,
22+
getTraces,
23+
invokeAllTestCases,
24+
splitSegmentsByName,
2525
} from '../helpers/tracesUtils';
2626
import {
2727
generateUniqueName,
2828
isValidRuntimeKey,
2929
} from '../../../commons/tests/utils/e2eUtils';
3030
import {
31-
RESOURCE_NAME_PREFIX,
32-
SETUP_TIMEOUT,
33-
TEARDOWN_TIMEOUT,
34-
TEST_CASE_TIMEOUT,
3531
expectedCustomAnnotationKey,
3632
expectedCustomAnnotationValue,
33+
expectedCustomErrorMessage,
3734
expectedCustomMetadataKey,
3835
expectedCustomMetadataValue,
3936
expectedCustomResponseValue,
40-
expectedCustomErrorMessage,
37+
RESOURCE_NAME_PREFIX,
38+
SETUP_TIMEOUT,
39+
TEARDOWN_TIMEOUT,
40+
TEST_CASE_TIMEOUT,
4141
} from './constants';
4242
import {
4343
assertAnnotation,
@@ -242,7 +242,7 @@ describe(`Tracer E2E tests, all features with decorator instantiation for runtim
242242
* 1. Lambda Context (AWS::Lambda)
243243
* 2. Lambda Function (AWS::Lambda::Function)
244244
* 4. DynamoDB (AWS::DynamoDB)
245-
* 4. Remote call (awslabs.github.io)
245+
* 4. Remote call (docs.powertools.aws.dev)
246246
*/
247247
expect(trace.Segments.length).toBe(4);
248248
const invocationSubsegment = getInvocationSubsegment(trace);
@@ -251,7 +251,7 @@ describe(`Tracer E2E tests, all features with decorator instantiation for runtim
251251
* Invocation subsegment should have a subsegment '## index.handler' (default behavior for Tracer)
252252
* '## index.handler' subsegment should have 3 subsegments
253253
* 1. DynamoDB (PutItem on the table)
254-
* 2. awslabs.github.io (Remote call)
254+
* 2. docs.powertools.aws.dev (Remote call)
255255
* 3. '### myMethod' (method decorator)
256256
*/
257257
const handlerSubsegment = getFirstSubsegment(invocationSubsegment);
@@ -263,11 +263,11 @@ describe(`Tracer E2E tests, all features with decorator instantiation for runtim
263263
}
264264
const subsegments = splitSegmentsByName(handlerSubsegment.subsegments, [
265265
'DynamoDB',
266-
'awslabs.github.io',
266+
'docs.powertools.aws.dev',
267267
'### myMethod',
268268
]);
269269
expect(subsegments.get('DynamoDB')?.length).toBe(1);
270-
expect(subsegments.get('awslabs.github.io')?.length).toBe(1);
270+
expect(subsegments.get('docs.powertools.aws.dev')?.length).toBe(1);
271271
expect(subsegments.get('### myMethod')?.length).toBe(1);
272272
expect(subsegments.get('other')?.length).toBe(0);
273273

@@ -350,7 +350,7 @@ describe(`Tracer E2E tests, all features with decorator instantiation for runtim
350350
* 1. Lambda Context (AWS::Lambda)
351351
* 2. Lambda Function (AWS::Lambda::Function)
352352
* 3. DynamoDB (AWS::DynamoDB)
353-
* 4. Remote call (awslabs.github.io)
353+
* 4. Remote call (docs.powertools.aws.dev)
354354
*/
355355
expect(trace.Segments.length).toBe(4);
356356
const invocationSubsegment = getInvocationSubsegment(trace);
@@ -359,7 +359,7 @@ describe(`Tracer E2E tests, all features with decorator instantiation for runtim
359359
* Invocation subsegment should have a subsegment '## index.handler' (default behavior for Tracer)
360360
* '## index.handler' subsegment should have 3 subsegments
361361
* 1. DynamoDB (PutItem on the table)
362-
* 2. awslabs.github.io (Remote call)
362+
* 2. docs.powertools.aws.dev (Remote call)
363363
* 3. '### myMethod' (method decorator)
364364
*/
365365
const handlerSubsegment = getFirstSubsegment(invocationSubsegment);
@@ -371,11 +371,11 @@ describe(`Tracer E2E tests, all features with decorator instantiation for runtim
371371
}
372372
const subsegments = splitSegmentsByName(handlerSubsegment.subsegments, [
373373
'DynamoDB',
374-
'awslabs.github.io',
374+
'docs.powertools.aws.dev',
375375
'### myMethod',
376376
]);
377377
expect(subsegments.get('DynamoDB')?.length).toBe(1);
378-
expect(subsegments.get('awslabs.github.io')?.length).toBe(1);
378+
expect(subsegments.get('docs.powertools.aws.dev')?.length).toBe(1);
379379
expect(subsegments.get('### myMethod')?.length).toBe(1);
380380
expect(subsegments.get('other')?.length).toBe(0);
381381

@@ -414,7 +414,7 @@ describe(`Tracer E2E tests, all features with decorator instantiation for runtim
414414
* 1. Lambda Context (AWS::Lambda)
415415
* 2. Lambda Function (AWS::Lambda::Function)
416416
* 3. DynamoDB (AWS::DynamoDB)
417-
* 4. Remote call (awslabs.github.io)
417+
* 4. Remote call (docs.powertools.aws.dev)
418418
*/
419419
expect(trace.Segments.length).toBe(4);
420420
const invocationSubsegment = getInvocationSubsegment(trace);
@@ -423,7 +423,7 @@ describe(`Tracer E2E tests, all features with decorator instantiation for runtim
423423
* Invocation subsegment should have a subsegment '## index.handler' (default behavior for Tracer)
424424
* '## index.handler' subsegment should have 3 subsegments
425425
* 1. DynamoDB (PutItem on the table)
426-
* 2. awslabs.github.io (Remote call)
426+
* 2. docs.powertools.aws.dev (Remote call)
427427
* 3. '### myMethod' (method decorator)
428428
*/
429429
const handlerSubsegment = getFirstSubsegment(invocationSubsegment);
@@ -439,11 +439,11 @@ describe(`Tracer E2E tests, all features with decorator instantiation for runtim
439439
}
440440
const subsegments = splitSegmentsByName(handlerSubsegment.subsegments, [
441441
'DynamoDB',
442-
'awslabs.github.io',
442+
'docs.powertools.aws.dev',
443443
'### myMethod',
444444
]);
445445
expect(subsegments.get('DynamoDB')?.length).toBe(1);
446-
expect(subsegments.get('awslabs.github.io')?.length).toBe(1);
446+
expect(subsegments.get('docs.powertools.aws.dev')?.length).toBe(1);
447447
expect(subsegments.get('### myMethod')?.length).toBe(1);
448448
expect(subsegments.get('other')?.length).toBe(0);
449449

‎packages/tracer/tests/e2e/allFeatures.manual.test.ts

+17-17
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
*/
66

77
import path from 'path';
8-
import { Table, AttributeType, BillingMode } from 'aws-cdk-lib/aws-dynamodb';
9-
import { App, Stack, RemovalPolicy } from 'aws-cdk-lib';
8+
import { AttributeType, BillingMode, Table } from 'aws-cdk-lib/aws-dynamodb';
9+
import { App, RemovalPolicy, Stack } from 'aws-cdk-lib';
1010
import { XRayClient } from '@aws-sdk/client-xray';
1111
import { STSClient } from '@aws-sdk/client-sts';
1212
import { v4 } from 'uuid';
@@ -15,34 +15,34 @@ import {
1515
destroyStack,
1616
} from '../../../commons/tests/utils/cdk-cli';
1717
import {
18-
getTraces,
19-
getInvocationSubsegment,
20-
splitSegmentsByName,
21-
invokeAllTestCases,
2218
createTracerTestFunction,
23-
getFunctionArn,
2419
getFirstSubsegment,
20+
getFunctionArn,
21+
getInvocationSubsegment,
22+
getTraces,
23+
invokeAllTestCases,
24+
splitSegmentsByName,
2525
} from '../helpers/tracesUtils';
2626
import type { ParsedTrace } from '../helpers/traceUtils.types';
2727
import {
2828
generateUniqueName,
2929
isValidRuntimeKey,
3030
} from '../../../commons/tests/utils/e2eUtils';
3131
import {
32-
RESOURCE_NAME_PREFIX,
33-
SETUP_TIMEOUT,
34-
TEARDOWN_TIMEOUT,
35-
TEST_CASE_TIMEOUT,
3632
expectedCustomAnnotationKey,
3733
expectedCustomAnnotationValue,
34+
expectedCustomErrorMessage,
3835
expectedCustomMetadataKey,
3936
expectedCustomMetadataValue,
4037
expectedCustomResponseValue,
41-
expectedCustomErrorMessage,
38+
RESOURCE_NAME_PREFIX,
39+
SETUP_TIMEOUT,
40+
TEARDOWN_TIMEOUT,
41+
TEST_CASE_TIMEOUT,
4242
} from './constants';
4343
import {
44-
assertErrorAndFault,
4544
assertAnnotation,
45+
assertErrorAndFault,
4646
} from '../helpers/traceAssertions';
4747

4848
const runtime: string = process.env.RUNTIME || 'nodejs18x';
@@ -146,7 +146,7 @@ describe(`Tracer E2E tests, all features with manual instantiation for runtime:
146146
* 1. Lambda Context (AWS::Lambda)
147147
* 2. Lambda Function (AWS::Lambda::Function)
148148
* 3. DynamoDB (AWS::DynamoDB)
149-
* 4. Remote call (awslabs.github.io)
149+
* 4. Remote call (docs.powertools.aws.dev)
150150
*/
151151
expect(trace.Segments.length).toBe(4);
152152
const invocationSubsegment = getInvocationSubsegment(trace);
@@ -155,7 +155,7 @@ describe(`Tracer E2E tests, all features with manual instantiation for runtime:
155155
* Invocation subsegment should have a subsegment '## index.handler' (default behavior for Tracer)
156156
* '## index.handler' subsegment should have 2 subsegments
157157
* 1. DynamoDB (PutItem on the table)
158-
* 2. awslabs.github.io (Remote call)
158+
* 2. docs.powertools.aws.dev (Remote call)
159159
*/
160160
const handlerSubsegment = getFirstSubsegment(invocationSubsegment);
161161
expect(handlerSubsegment.name).toBe('## index.handler');
@@ -166,10 +166,10 @@ describe(`Tracer E2E tests, all features with manual instantiation for runtime:
166166
}
167167
const subsegments = splitSegmentsByName(handlerSubsegment.subsegments, [
168168
'DynamoDB',
169-
'awslabs.github.io',
169+
'docs.powertools.aws.dev',
170170
]);
171171
expect(subsegments.get('DynamoDB')?.length).toBe(1);
172-
expect(subsegments.get('awslabs.github.io')?.length).toBe(1);
172+
expect(subsegments.get('docs.powertools.aws.dev')?.length).toBe(1);
173173
expect(subsegments.get('other')?.length).toBe(0);
174174

175175
const shouldThrowAnError = i === invocations - 1;

‎packages/tracer/tests/e2e/allFeatures.middy.test.ts

+24-24
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
*/
66

77
import path from 'path';
8-
import { Table, AttributeType, BillingMode } from 'aws-cdk-lib/aws-dynamodb';
9-
import { App, Stack, RemovalPolicy } from 'aws-cdk-lib';
8+
import { AttributeType, BillingMode, Table } from 'aws-cdk-lib/aws-dynamodb';
9+
import { App, RemovalPolicy, Stack } from 'aws-cdk-lib';
1010
import { XRayClient } from '@aws-sdk/client-xray';
1111
import { STSClient } from '@aws-sdk/client-sts';
1212
import { v4 } from 'uuid';
@@ -15,29 +15,29 @@ import {
1515
destroyStack,
1616
} from '../../../commons/tests/utils/cdk-cli';
1717
import {
18-
getTraces,
19-
getInvocationSubsegment,
20-
splitSegmentsByName,
21-
invokeAllTestCases,
2218
createTracerTestFunction,
23-
getFunctionArn,
2419
getFirstSubsegment,
20+
getFunctionArn,
21+
getInvocationSubsegment,
22+
getTraces,
23+
invokeAllTestCases,
24+
splitSegmentsByName,
2525
} from '../helpers/tracesUtils';
2626
import {
2727
generateUniqueName,
2828
isValidRuntimeKey,
2929
} from '../../../commons/tests/utils/e2eUtils';
3030
import {
31-
RESOURCE_NAME_PREFIX,
32-
SETUP_TIMEOUT,
33-
TEARDOWN_TIMEOUT,
34-
TEST_CASE_TIMEOUT,
3531
expectedCustomAnnotationKey,
3632
expectedCustomAnnotationValue,
33+
expectedCustomErrorMessage,
3734
expectedCustomMetadataKey,
3835
expectedCustomMetadataValue,
3936
expectedCustomResponseValue,
40-
expectedCustomErrorMessage,
37+
RESOURCE_NAME_PREFIX,
38+
SETUP_TIMEOUT,
39+
TEARDOWN_TIMEOUT,
40+
TEST_CASE_TIMEOUT,
4141
} from './constants';
4242
import {
4343
assertAnnotation,
@@ -246,7 +246,7 @@ describe(`Tracer E2E tests, all features with middy instantiation for runtime: $
246246
* 1. Lambda Context (AWS::Lambda)
247247
* 2. Lambda Function (AWS::Lambda::Function)
248248
* 3. DynamoDB Table (AWS::DynamoDB::Table)
249-
* 4. Remote call (awslabs.github.io)
249+
* 4. Remote call (docs.powertools.aws.dev)
250250
*/
251251
expect(trace.Segments.length).toBe(4);
252252
const invocationSubsegment = getInvocationSubsegment(trace);
@@ -255,7 +255,7 @@ describe(`Tracer E2E tests, all features with middy instantiation for runtime: $
255255
* Invocation subsegment should have a subsegment '## index.handler' (default behavior for Tracer)
256256
* '## index.handler' subsegment should have 2 subsegments
257257
* 1. DynamoDB (PutItem on the table)
258-
* 2. awslabs.github.io (Remote call)
258+
* 2. docs.powertools.aws.dev (Remote call)
259259
*/
260260
const handlerSubsegment = getFirstSubsegment(invocationSubsegment);
261261
expect(handlerSubsegment.name).toBe('## index.handler');
@@ -266,10 +266,10 @@ describe(`Tracer E2E tests, all features with middy instantiation for runtime: $
266266
}
267267
const subsegments = splitSegmentsByName(handlerSubsegment.subsegments, [
268268
'DynamoDB',
269-
'awslabs.github.io',
269+
'docs.powertools.aws.dev',
270270
]);
271271
expect(subsegments.get('DynamoDB')?.length).toBe(1);
272-
expect(subsegments.get('awslabs.github.io')?.length).toBe(1);
272+
expect(subsegments.get('docs.powertools.aws.dev')?.length).toBe(1);
273273
expect(subsegments.get('other')?.length).toBe(0);
274274

275275
const shouldThrowAnError = i === invocations - 1;
@@ -351,7 +351,7 @@ describe(`Tracer E2E tests, all features with middy instantiation for runtime: $
351351
* 1. Lambda Context (AWS::Lambda)
352352
* 2. Lambda Function (AWS::Lambda::Function)
353353
* 3. DynamoDB Table (AWS::DynamoDB::Table)
354-
* 4. Remote call (awslabs.github.io)
354+
* 4. Remote call (docs.powertools.aws.dev)
355355
*/
356356
expect(trace.Segments.length).toBe(4);
357357
const invocationSubsegment = getInvocationSubsegment(trace);
@@ -360,7 +360,7 @@ describe(`Tracer E2E tests, all features with middy instantiation for runtime: $
360360
* Invocation subsegment should have a subsegment '## index.handler' (default behavior for Tracer)
361361
* '## index.handler' subsegment should have 2 subsegments
362362
* 1. DynamoDB (PutItem on the table)
363-
* 2. awslabs.github.io (Remote call)
363+
* 2. docs.powertools.aws.dev (Remote call)
364364
*/
365365
const handlerSubsegment = getFirstSubsegment(invocationSubsegment);
366366
expect(handlerSubsegment.name).toBe('## index.handler');
@@ -371,10 +371,10 @@ describe(`Tracer E2E tests, all features with middy instantiation for runtime: $
371371
}
372372
const subsegments = splitSegmentsByName(handlerSubsegment.subsegments, [
373373
'DynamoDB',
374-
'awslabs.github.io',
374+
'docs.powertools.aws.dev',
375375
]);
376376
expect(subsegments.get('DynamoDB')?.length).toBe(1);
377-
expect(subsegments.get('awslabs.github.io')?.length).toBe(1);
377+
expect(subsegments.get('docs.powertools.aws.dev')?.length).toBe(1);
378378
expect(subsegments.get('other')?.length).toBe(0);
379379

380380
const shouldThrowAnError = i === invocations - 1;
@@ -415,7 +415,7 @@ describe(`Tracer E2E tests, all features with middy instantiation for runtime: $
415415
* 1. Lambda Context (AWS::Lambda)
416416
* 2. Lambda Function (AWS::Lambda::Function)
417417
* 3. DynamoDB Table (AWS::DynamoDB::Table)
418-
* 4. Remote call (awslabs.github.io)
418+
* 4. Remote call (docs.powertools.aws.dev)
419419
*/
420420
expect(trace.Segments.length).toBe(4);
421421
const invocationSubsegment = getInvocationSubsegment(trace);
@@ -424,7 +424,7 @@ describe(`Tracer E2E tests, all features with middy instantiation for runtime: $
424424
* Invocation subsegment should have a subsegment '## index.handlerWithNoCaptureResponseViaMiddlewareOption' (default behavior for Tracer)
425425
* '## index.handlerWithNoCaptureResponseViaMiddlewareOption' subsegment should have 2 subsegments
426426
* 1. DynamoDB (PutItem on the table)
427-
* 2. awslabs.github.io (Remote call)
427+
* 2. docs.powertools.aws.dev (Remote call)
428428
*/
429429
const handlerSubsegment = getFirstSubsegment(invocationSubsegment);
430430
expect(handlerSubsegment.name).toBe(
@@ -439,10 +439,10 @@ describe(`Tracer E2E tests, all features with middy instantiation for runtime: $
439439
}
440440
const subsegments = splitSegmentsByName(handlerSubsegment.subsegments, [
441441
'DynamoDB',
442-
'awslabs.github.io',
442+
'docs.powertools.aws.dev',
443443
]);
444444
expect(subsegments.get('DynamoDB')?.length).toBe(1);
445-
expect(subsegments.get('awslabs.github.io')?.length).toBe(1);
445+
expect(subsegments.get('docs.powertools.aws.dev')?.length).toBe(1);
446446
expect(subsegments.get('other')?.length).toBe(0);
447447

448448
const shouldThrowAnError = i === invocations - 1;

‎packages/tracer/tests/e2e/asyncHandler.decorator.test.ts

+20-20
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
*/
66

77
import path from 'path';
8-
import { Table, AttributeType, BillingMode } from 'aws-cdk-lib/aws-dynamodb';
9-
import { App, Stack, RemovalPolicy } from 'aws-cdk-lib';
8+
import { AttributeType, BillingMode, Table } from 'aws-cdk-lib/aws-dynamodb';
9+
import { App, RemovalPolicy, Stack } from 'aws-cdk-lib';
1010
import { XRayClient } from '@aws-sdk/client-xray';
1111
import { STSClient } from '@aws-sdk/client-sts';
1212
import { v4 } from 'uuid';
@@ -15,30 +15,30 @@ import {
1515
destroyStack,
1616
} from '../../../commons/tests/utils/cdk-cli';
1717
import {
18-
getTraces,
19-
getInvocationSubsegment,
20-
splitSegmentsByName,
21-
invokeAllTestCases,
2218
createTracerTestFunction,
23-
getFunctionArn,
2419
getFirstSubsegment,
20+
getFunctionArn,
21+
getInvocationSubsegment,
22+
getTraces,
23+
invokeAllTestCases,
24+
splitSegmentsByName,
2525
} from '../helpers/tracesUtils';
2626
import {
2727
generateUniqueName,
2828
isValidRuntimeKey,
2929
} from '../../../commons/tests/utils/e2eUtils';
3030
import {
31-
RESOURCE_NAME_PREFIX,
32-
SETUP_TIMEOUT,
33-
TEARDOWN_TIMEOUT,
34-
TEST_CASE_TIMEOUT,
35-
expectedCustomErrorMessage,
3631
expectedCustomAnnotationKey,
3732
expectedCustomAnnotationValue,
33+
expectedCustomErrorMessage,
3834
expectedCustomMetadataKey,
3935
expectedCustomMetadataValue,
4036
expectedCustomResponseValue,
4137
expectedCustomSubSegmentName,
38+
RESOURCE_NAME_PREFIX,
39+
SETUP_TIMEOUT,
40+
TEARDOWN_TIMEOUT,
41+
TEST_CASE_TIMEOUT,
4242
} from './constants';
4343
import {
4444
assertAnnotation,
@@ -179,7 +179,7 @@ describe(`Tracer E2E tests, asynchronous handler with decorator instantiation fo
179179
* 1. Lambda Context (AWS::Lambda)
180180
* 2. Lambda Function (AWS::Lambda::Function)
181181
* 3. DynamoDB Table (AWS::DynamoDB::Table)
182-
* 4. Remote call (awslabs.github.io)
182+
* 4. Remote call (docs.powertools.aws.dev)
183183
*/
184184
expect(trace.Segments.length).toBe(4);
185185
const invocationSubsegment = getInvocationSubsegment(trace);
@@ -188,7 +188,7 @@ describe(`Tracer E2E tests, asynchronous handler with decorator instantiation fo
188188
* Invocation subsegment should have a subsegment '## index.handler' (default behavior for Tracer)
189189
* '## index.handler' subsegment should have 3 subsegments
190190
* 1. DynamoDB (PutItem on the table)
191-
* 2. awslabs.github.io (Remote call)
191+
* 2. docs.powertools.aws.dev (Remote call)
192192
* 3. '### myMethod' (method decorator)
193193
*/
194194
const handlerSubsegment = getFirstSubsegment(invocationSubsegment);
@@ -200,11 +200,11 @@ describe(`Tracer E2E tests, asynchronous handler with decorator instantiation fo
200200
}
201201
const subsegments = splitSegmentsByName(handlerSubsegment.subsegments, [
202202
'DynamoDB',
203-
'awslabs.github.io',
203+
'docs.powertools.aws.dev',
204204
'### myMethod',
205205
]);
206206
expect(subsegments.get('DynamoDB')?.length).toBe(1);
207-
expect(subsegments.get('awslabs.github.io')?.length).toBe(1);
207+
expect(subsegments.get('docs.powertools.aws.dev')?.length).toBe(1);
208208
expect(subsegments.get('### myMethod')?.length).toBe(1);
209209
expect(subsegments.get('other')?.length).toBe(0);
210210

@@ -287,7 +287,7 @@ describe(`Tracer E2E tests, asynchronous handler with decorator instantiation fo
287287
* 1. Lambda Context (AWS::Lambda)
288288
* 2. Lambda Function (AWS::Lambda::Function)
289289
* 3. DynamoDB Table (AWS::DynamoDB::Table)
290-
* 4. Remote call (awslabs.github.io)
290+
* 4. Remote call (docs.powertools.aws.dev)
291291
*/
292292
expect(trace.Segments.length).toBe(4);
293293
const invocationSubsegment = getInvocationSubsegment(trace);
@@ -296,7 +296,7 @@ describe(`Tracer E2E tests, asynchronous handler with decorator instantiation fo
296296
* Invocation subsegment should have a subsegment '## index.handler' (default behavior for Tracer)
297297
* '## index.handler' subsegment should have 3 subsegments
298298
* 1. DynamoDB (PutItem on the table)
299-
* 2. awslabs.github.io (Remote call)
299+
* 2. docs.powertools.aws.dev (Remote call)
300300
* 3. '### mySubsegment' (method decorator with custom name)
301301
*/
302302
const handlerSubsegment = getFirstSubsegment(invocationSubsegment);
@@ -310,11 +310,11 @@ describe(`Tracer E2E tests, asynchronous handler with decorator instantiation fo
310310
}
311311
const subsegments = splitSegmentsByName(handlerSubsegment.subsegments, [
312312
'DynamoDB',
313-
'awslabs.github.io',
313+
'docs.powertools.aws.dev',
314314
expectedCustomSubSegmentName,
315315
]);
316316
expect(subsegments.get('DynamoDB')?.length).toBe(1);
317-
expect(subsegments.get('awslabs.github.io')?.length).toBe(1);
317+
expect(subsegments.get('docs.powertools.aws.dev')?.length).toBe(1);
318318
expect(subsegments.get(expectedCustomSubSegmentName)?.length).toBe(1);
319319
expect(subsegments.get('other')?.length).toBe(0);
320320

‎packages/tracer/tests/helpers/tracesUtils.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,25 @@ import promiseRetry from 'promise-retry';
22
import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs';
33
import { Duration } from 'aws-cdk-lib';
44
import { Architecture, Tracing } from 'aws-cdk-lib/aws-lambda';
5+
import type { XRayClient } from '@aws-sdk/client-xray';
56
import {
6-
GetTraceSummariesCommand,
77
BatchGetTracesCommand,
8+
GetTraceSummariesCommand,
89
} from '@aws-sdk/client-xray';
9-
import type { XRayClient } from '@aws-sdk/client-xray';
1010
import type { STSClient } from '@aws-sdk/client-sts';
1111
import { GetCallerIdentityCommand } from '@aws-sdk/client-sts';
1212
import {
1313
expectedCustomAnnotationKey,
1414
expectedCustomAnnotationValue,
15+
expectedCustomErrorMessage,
1516
expectedCustomMetadataKey,
1617
expectedCustomMetadataValue,
1718
expectedCustomResponseValue,
18-
expectedCustomErrorMessage,
1919
} from '../e2e/constants';
2020
import {
2121
invokeFunction,
22-
TestRuntimesKey,
2322
TEST_RUNTIMES,
23+
TestRuntimesKey,
2424
} from '../../../commons/tests/utils/e2eUtils';
2525
import { FunctionSegmentNotDefinedError } from './FunctionSegmentNotDefinedError';
2626
import type {

0 commit comments

Comments
 (0)
Please sign in to comment.