From b9fcef66eb4bd9a7ad1eeac5f5db2cdbccc70c71 Mon Sep 17 00:00:00 2001 From: Alexander Schueren Date: Tue, 20 Jun 2023 12:27:02 +0200 Subject: [PATCH] fix(idempotency): skip persistence for optional idempotency key (#1507) * add skip idempotency step in the handler in specific cases --- package.json | 1 + packages/commons/tests/utils/e2eUtils.ts | 12 +--- packages/idempotency/src/IdempotencyConfig.ts | 2 + .../idempotency/src/IdempotencyHandler.ts | 31 ++++++++++ .../src/middleware/makeHandlerIdempotent.ts | 24 +++++++- .../src/persistence/BasePersistenceLayer.ts | 7 ++- .../idempotencyDecorator.test.FunctionCode.ts | 21 ++++++- .../tests/e2e/idempotencyDecorator.test.ts | 40 ++++++++++++- .../tests/unit/IdempotencyHandler.test.ts | 38 ++++++++++++ .../tests/unit/makeHandlerIdempotent.test.ts | 60 ++++++++++++++++++- .../persistence/BasePersistenceLayer.test.ts | 2 +- .../tests/e2e/basicFeatures.middy.test.ts | 4 +- .../tests/e2e/allFeatures.decorator.test.ts | 48 +++++++-------- .../tests/e2e/allFeatures.manual.test.ts | 34 +++++------ .../tests/e2e/allFeatures.middy.test.ts | 48 +++++++-------- .../tests/e2e/asyncHandler.decorator.test.ts | 40 ++++++------- packages/tracer/tests/helpers/tracesUtils.ts | 8 +-- 17 files changed, 311 insertions(+), 109 deletions(-) diff --git a/package.json b/package.json index 03e6b8b7a5..6f0304cb42 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "scripts": { "init-environment": "husky install", "test": "npm t -ws", + "test:e2e": "npm run test:e2e -ws", "commit": "commit", "package": "npm run package -ws", "setup-local": "npm ci && npm run build && npm run init-environment", diff --git a/packages/commons/tests/utils/e2eUtils.ts b/packages/commons/tests/utils/e2eUtils.ts index b00810d96d..d8f32ad607 100644 --- a/packages/commons/tests/utils/e2eUtils.ts +++ b/packages/commons/tests/utils/e2eUtils.ts @@ -2,7 +2,7 @@ * E2E utils is used by e2e tests. They are helper function that calls either CDK or SDK * to interact with services. */ -import { App, CfnOutput, Stack, Duration } from 'aws-cdk-lib'; +import { App, CfnOutput, Duration, Stack } from 'aws-cdk-lib'; import { NodejsFunction, NodejsFunctionProps, @@ -91,15 +91,11 @@ export const invokeFunction = async ( ): Promise => { const invocationLogs: InvocationLogs[] = []; - const promiseFactory = ( - index?: number, - includeIndex = true - ): Promise => { + const promiseFactory = (index?: number): Promise => { // in some cases we need to send a payload without the index, i.e. idempotency tests const payloadToSend = includeIndex ? { invocation: index, ...payload } : { ...payload }; - const invokePromise = lambdaClient .send( new InvokeCommand({ @@ -126,9 +122,7 @@ export const invokeFunction = async ( const invocation = invocationMode == 'PARALLEL' - ? Promise.all( - promiseFactories.map((factory, index) => factory(index, includeIndex)) - ) + ? Promise.all(promiseFactories.map((factory, index) => factory(index))) : chainPromises(promiseFactories); await invocation; diff --git a/packages/idempotency/src/IdempotencyConfig.ts b/packages/idempotency/src/IdempotencyConfig.ts index ad494bdc64..95b8a96a99 100644 --- a/packages/idempotency/src/IdempotencyConfig.ts +++ b/packages/idempotency/src/IdempotencyConfig.ts @@ -38,6 +38,8 @@ class IdempotencyConfig { /** * Throw an error if the idempotency key is not found in the event. * In some cases, you may want to allow the request to continue without idempotency. + * If set to false and idempotency key is not found, the request will continue without idempotency. + * @default false */ public throwOnNoIdempotencyKey: boolean; /** diff --git a/packages/idempotency/src/IdempotencyHandler.ts b/packages/idempotency/src/IdempotencyHandler.ts index 66ecae19eb..7fece6ba25 100644 --- a/packages/idempotency/src/IdempotencyHandler.ts +++ b/packages/idempotency/src/IdempotencyHandler.ts @@ -9,6 +9,7 @@ import { import { BasePersistenceLayer, IdempotencyRecord } from './persistence'; import { IdempotencyConfig } from './IdempotencyConfig'; import { MAX_RETRIES } from './constants'; +import { search } from 'jmespath'; /** * @internal @@ -127,6 +128,17 @@ export class IdempotencyHandler { } public async processIdempotency(): Promise { + // early return if we should skip idempotency completely + if ( + IdempotencyHandler.shouldSkipIdempotency( + this.idempotencyConfig.eventKeyJmesPath, + this.idempotencyConfig.throwOnNoIdempotencyKey, + this.fullFunctionPayload + ) + ) { + return await this.functionToMakeIdempotent(this.fullFunctionPayload); + } + try { await this.persistenceStore.saveInProgress( this.functionPayloadToBeHashed @@ -146,4 +158,23 @@ export class IdempotencyHandler { return this.getFunctionResult(); } + + /** + * avoid idempotency if the eventKeyJmesPath is not present in the payload and throwOnNoIdempotencyKey is false + * static so {@link makeHandlerIdempotent} middleware can use it + * TOOD: refactor so middy uses IdempotencyHandler internally wihtout reimplementing the logic + * @param eventKeyJmesPath + * @param throwOnNoIdempotencyKey + * @param fullFunctionPayload + * @private + */ + public static shouldSkipIdempotency( + eventKeyJmesPath: string, + throwOnNoIdempotencyKey: boolean, + fullFunctionPayload: Record + ): boolean { + return (eventKeyJmesPath && + !throwOnNoIdempotencyKey && + !search(fullFunctionPayload, eventKeyJmesPath)) as boolean; + } } diff --git a/packages/idempotency/src/middleware/makeHandlerIdempotent.ts b/packages/idempotency/src/middleware/makeHandlerIdempotent.ts index 38e2eaa704..5af5b2ab23 100644 --- a/packages/idempotency/src/middleware/makeHandlerIdempotent.ts +++ b/packages/idempotency/src/middleware/makeHandlerIdempotent.ts @@ -2,9 +2,9 @@ import { IdempotencyHandler } from '../IdempotencyHandler'; import { IdempotencyConfig } from '../IdempotencyConfig'; import { cleanupMiddlewares } from '@aws-lambda-powertools/commons/lib/middleware'; import { + IdempotencyInconsistentStateError, IdempotencyItemAlreadyExistsError, IdempotencyPersistenceLayerError, - IdempotencyInconsistentStateError, } from '../Exceptions'; import { IdempotencyRecord } from '../persistence'; import { MAX_RETRIES } from '../constants'; @@ -50,6 +50,9 @@ const makeHandlerIdempotent = ( config: idempotencyConfig, }); + // keep the flag for after and onError checks + let shouldSkipIdempotency = false; + /** * Function called before the handler is executed. * @@ -72,6 +75,18 @@ const makeHandlerIdempotent = ( request: MiddyLikeRequest, retryNo = 0 ): Promise => { + if ( + IdempotencyHandler.shouldSkipIdempotency( + idempotencyConfig.eventKeyJmesPath, + idempotencyConfig.throwOnNoIdempotencyKey, + request.event as Record + ) + ) { + // set the flag to skip checks in after and onError + shouldSkipIdempotency = true; + + return; + } try { await persistenceStore.saveInProgress( request.event as Record, @@ -114,7 +129,6 @@ const makeHandlerIdempotent = ( } } }; - /** * Function called after the handler has executed successfully. * @@ -125,6 +139,9 @@ const makeHandlerIdempotent = ( * @param request - The Middy request object */ const after = async (request: MiddyLikeRequest): Promise => { + if (shouldSkipIdempotency) { + return; + } try { await persistenceStore.saveSuccess( request.event as Record, @@ -146,6 +163,9 @@ const makeHandlerIdempotent = ( * @param request - The Middy request object */ const onError = async (request: MiddyLikeRequest): Promise => { + if (shouldSkipIdempotency) { + return; + } try { await persistenceStore.deleteRecord( request.event as Record diff --git a/packages/idempotency/src/persistence/BasePersistenceLayer.ts b/packages/idempotency/src/persistence/BasePersistenceLayer.ts index 66ab7ca085..3f4a41ae29 100644 --- a/packages/idempotency/src/persistence/BasePersistenceLayer.ts +++ b/packages/idempotency/src/persistence/BasePersistenceLayer.ts @@ -1,7 +1,7 @@ import { createHash, Hash } from 'node:crypto'; import { search } from 'jmespath'; -import { IdempotencyRecordStatus } from '../types'; import type { BasePersistenceLayerOptions } from '../types'; +import { IdempotencyRecordStatus } from '../types'; import { EnvironmentVariablesService } from '../config'; import { IdempotencyRecord } from './IdempotencyRecord'; import { BasePersistenceLayerInterface } from './BasePersistenceLayerInterface'; @@ -176,10 +176,13 @@ abstract class BasePersistenceLayer implements BasePersistenceLayerInterface { } protected abstract _deleteRecord(record: IdempotencyRecord): Promise; + protected abstract _getRecord( idempotencyKey: string ): Promise; + protected abstract _putRecord(record: IdempotencyRecord): Promise; + protected abstract _updateRecord(record: IdempotencyRecord): Promise; private deleteFromCache(idempotencyKey: string): void { @@ -294,7 +297,7 @@ abstract class BasePersistenceLayer implements BasePersistenceLayerInterface { * Save record to local cache except for when status is `INPROGRESS`. * * We can't cache `INPROGRESS` records because we have no way to reflect updates - * that might happen to the record outside of the execution context of the function. + * that might happen to the record outside the execution context of the function. * * @param record - record to save */ diff --git a/packages/idempotency/tests/e2e/idempotencyDecorator.test.FunctionCode.ts b/packages/idempotency/tests/e2e/idempotencyDecorator.test.FunctionCode.ts index 244457aab3..9d6d0b4471 100644 --- a/packages/idempotency/tests/e2e/idempotencyDecorator.test.FunctionCode.ts +++ b/packages/idempotency/tests/e2e/idempotencyDecorator.test.FunctionCode.ts @@ -3,6 +3,7 @@ import { LambdaInterface } from '@aws-lambda-powertools/commons'; import { idempotentFunction, idempotentLambdaHandler } from '../../src'; import { Logger } from '../../../logger'; import { DynamoDBPersistenceLayer } from '../../src/persistence/DynamoDBPersistenceLayer'; +import { IdempotencyConfig } from '../../src/'; const IDEMPOTENCY_TABLE_NAME = process.env.IDEMPOTENCY_TABLE_NAME || 'table_name'; @@ -62,10 +63,25 @@ class DefaultLambda implements LambdaInterface { _context: Context ): Promise { logger.info(`Got test event: ${JSON.stringify(_event)}`); - // sleep for 5 seconds throw new Error('Failed'); } + + @idempotentLambdaHandler({ + persistenceStore: dynamoDBPersistenceLayer, + config: new IdempotencyConfig({ + eventKeyJmesPath: 'idempotencyKey', + throwOnNoIdempotencyKey: false, + }), + }) + public async handlerWithOptionalIdempoitencyKey( + _event: TestEvent, + _context: Context + ): Promise { + logger.info(`Got test event: ${JSON.stringify(_event)}`); + + return 'This should not be stored in DynamoDB'; + } } const defaultLambda = new DefaultLambda(); @@ -74,6 +90,9 @@ export const handlerCustomized = defaultLambda.handlerCustomized.bind(defaultLambda); export const handlerFails = defaultLambda.handlerFails.bind(defaultLambda); +export const handlerWithOptionalIdempoitencyKey = + defaultLambda.handlerWithOptionalIdempoitencyKey.bind(defaultLambda); + const logger = new Logger(); class LambdaWithKeywordArgument implements LambdaInterface { diff --git a/packages/idempotency/tests/e2e/idempotencyDecorator.test.ts b/packages/idempotency/tests/e2e/idempotencyDecorator.test.ts index 6f028ac203..9b2fb5c26a 100644 --- a/packages/idempotency/tests/e2e/idempotencyDecorator.test.ts +++ b/packages/idempotency/tests/e2e/idempotencyDecorator.test.ts @@ -22,7 +22,7 @@ import { destroyStack, } from '../../../commons/tests/utils/cdk-cli'; import { LEVEL } from '../../../commons/tests/utils/InvocationLogs'; -import { GetCommand } from '@aws-sdk/lib-dynamodb'; +import { GetCommand, ScanCommand } from '@aws-sdk/lib-dynamodb'; import { createHash } from 'node:crypto'; import { createIdempotencyResources } from '../helpers/idempotencyUtils'; @@ -110,6 +110,23 @@ createIdempotencyResources( functionNameFails, 'handlerFails' ); + +const functionNameOptionalIdempotencyKey = generateUniqueName( + RESOURCE_NAME_PREFIX, + uuid, + runtime, + 'optionalIdempotencyKey' +); +const ddbTableNameOptionalIdempotencyKey = + stackName + '-optional-idempotencyKey-table'; +createIdempotencyResources( + stack, + runtime, + ddbTableNameOptionalIdempotencyKey, + decoratorFunctionFile, + functionNameOptionalIdempotencyKey, + 'handlerWithOptionalIdempoitencyKey' +); describe('Idempotency e2e test decorator, default settings', () => { beforeAll(async () => { await deployStack(app, stack); @@ -285,6 +302,27 @@ describe('Idempotency e2e test decorator, default settings', () => { TEST_CASE_TIMEOUT ); + test( + 'when called with a function with optional idempotency key and thorwOnNoIdempotencyKey is false, it does not create ddb entry', + async () => { + const payload = { foo: 'baz' }; // we set eventKeyJmesPath: 'idempotencyKey' in the idempotency configuration + await invokeFunction( + functionNameOptionalIdempotencyKey, + 2, + 'PARALLEL', + payload, + false + ); + const result = await ddb.send( + new ScanCommand({ + TableName: ddbTableNameOptionalIdempotencyKey, + }) + ); + expect(result?.Items).toEqual([]); + }, + TEST_CASE_TIMEOUT + ); + afterAll(async () => { if (!process.env.DISABLE_TEARDOWN) { await destroyStack(app, stack); diff --git a/packages/idempotency/tests/unit/IdempotencyHandler.test.ts b/packages/idempotency/tests/unit/IdempotencyHandler.test.ts index 8f4b4c3a0d..be0dada29e 100644 --- a/packages/idempotency/tests/unit/IdempotencyHandler.test.ts +++ b/packages/idempotency/tests/unit/IdempotencyHandler.test.ts @@ -209,6 +209,44 @@ describe('Class IdempotencyHandler', () => { expect(mockGetRecord).toHaveBeenCalledTimes(1); expect(mockDetermineResultFromIdempotencyRecord).toHaveBeenCalledTimes(1); }); + + test('when throwOnNoIdempotencyKey is false and the key is missing, we skip idempotency', async () => { + const idempotentHandlerSkips = new IdempotencyHandler({ + functionToMakeIdempotent: mockFunctionToMakeIdempotent, + functionPayloadToBeHashed: mockFunctionPayloadToBeHashed, + persistenceStore: mockIdempotencyOptions.persistenceStore, + fullFunctionPayload: mockFullFunctionPayload, + idempotencyConfig: new IdempotencyConfig({ + throwOnNoIdempotencyKey: false, + eventKeyJmesPath: 'idempotencyKey', + }), + }); + + const mockSaveInProgress = jest.spyOn( + mockIdempotencyOptions.persistenceStore, + 'saveInProgress' + ); + + const mockSaveSuccessfulResult = jest.spyOn( + mockIdempotencyOptions.persistenceStore, + 'saveSuccess' + ); + const mockGetRecord = jest.spyOn( + mockIdempotencyOptions.persistenceStore, + 'getRecord' + ); + + mockFunctionToMakeIdempotent.mockImplementation(() => { + return 'result'; + }); + + await expect(idempotentHandlerSkips.processIdempotency()).resolves.toBe( + 'result' + ); + expect(mockSaveInProgress).toHaveBeenCalledTimes(0); + expect(mockGetRecord).toHaveBeenCalledTimes(0); + expect(mockSaveSuccessfulResult).toHaveBeenCalledTimes(0); + }); }); describe('Method: getFunctionResult', () => { diff --git a/packages/idempotency/tests/unit/makeHandlerIdempotent.test.ts b/packages/idempotency/tests/unit/makeHandlerIdempotent.test.ts index c3677032bc..b36dbaf1be 100644 --- a/packages/idempotency/tests/unit/makeHandlerIdempotent.test.ts +++ b/packages/idempotency/tests/unit/makeHandlerIdempotent.test.ts @@ -9,9 +9,9 @@ import { Custom as dummyEvent } from '../../../commons/src/samples/resources/eve import { IdempotencyRecordStatus } from '../../src/types'; import { IdempotencyRecord } from '../../src/persistence'; import { - IdempotencyPersistenceLayerError, - IdempotencyItemAlreadyExistsError, IdempotencyInconsistentStateError, + IdempotencyItemAlreadyExistsError, + IdempotencyPersistenceLayerError, } from '../../src/Exceptions'; import { IdempotencyConfig } from '../../src/'; import middy from '@middy/core'; @@ -268,4 +268,60 @@ describe('Middleware: makeHandlerIdempotent', () => { expect(saveInProgressSpy).toHaveBeenCalledTimes(0); expect(saveSuccessSpy).toHaveBeenCalledTimes(0); }); + + it('skips idempotency if no idempotency key is provided and throwOnNoIdempotencyKey is false', async () => { + // Prepare + const handler = middy( + async (_event: unknown, _context: Context): Promise => true + ).use( + makeHandlerIdempotent({ + ...mockIdempotencyOptions, + config: new IdempotencyConfig({ + eventKeyJmesPath: 'idempotencyKey', + throwOnNoIdempotencyKey: false, + }), + }) + ); + const saveInProgressSpy = jest.spyOn( + mockIdempotencyOptions.persistenceStore, + 'saveInProgress' + ); + const saveSuccessSpy = jest.spyOn( + mockIdempotencyOptions.persistenceStore, + 'saveSuccess' + ); + + // Act + const result = await handler(event, context); + + // Assess + expect(result).toBe(true); + expect(saveInProgressSpy).toHaveBeenCalledTimes(0); + expect(saveSuccessSpy).toHaveBeenCalledTimes(0); + }); + + it(' skips idempotency if error is thrown in the middleware', async () => { + const handler = middy( + async (_event: unknown, _context: Context): Promise => { + throw new Error('Something went wrong'); + } + ).use( + makeHandlerIdempotent({ + ...mockIdempotencyOptions, + config: new IdempotencyConfig({ + eventKeyJmesPath: 'idempotencyKey', + throwOnNoIdempotencyKey: false, + }), + }) + ); + + const deleteRecordSpy = jest.spyOn( + mockIdempotencyOptions.persistenceStore, + 'deleteRecord' + ); + + await expect(handler(event, context)).rejects.toThrowError(); + + expect(deleteRecordSpy).toHaveBeenCalledTimes(0); + }); }); diff --git a/packages/idempotency/tests/unit/persistence/BasePersistenceLayer.test.ts b/packages/idempotency/tests/unit/persistence/BasePersistenceLayer.test.ts index 93133bc11d..9cc3dc63f5 100644 --- a/packages/idempotency/tests/unit/persistence/BasePersistenceLayer.test.ts +++ b/packages/idempotency/tests/unit/persistence/BasePersistenceLayer.test.ts @@ -6,8 +6,8 @@ import { ContextExamples as dummyContext } from '@aws-lambda-powertools/commons'; import { IdempotencyConfig } from '../../../src'; import { - IdempotencyRecord, BasePersistenceLayer, + IdempotencyRecord, } from '../../../src/persistence'; import { IdempotencyItemAlreadyExistsError, diff --git a/packages/logger/tests/e2e/basicFeatures.middy.test.ts b/packages/logger/tests/e2e/basicFeatures.middy.test.ts index 82a849f6b5..7f16412182 100644 --- a/packages/logger/tests/e2e/basicFeatures.middy.test.ts +++ b/packages/logger/tests/e2e/basicFeatures.middy.test.ts @@ -20,10 +20,10 @@ import { } from '../../../commons/tests/utils/cdk-cli'; import { RESOURCE_NAME_PREFIX, - STACK_OUTPUT_LOG_GROUP, SETUP_TIMEOUT, - TEST_CASE_TIMEOUT, + STACK_OUTPUT_LOG_GROUP, TEARDOWN_TIMEOUT, + TEST_CASE_TIMEOUT, XRAY_TRACE_ID_REGEX, } from './constants'; diff --git a/packages/tracer/tests/e2e/allFeatures.decorator.test.ts b/packages/tracer/tests/e2e/allFeatures.decorator.test.ts index fb8ed7a972..43594d7791 100644 --- a/packages/tracer/tests/e2e/allFeatures.decorator.test.ts +++ b/packages/tracer/tests/e2e/allFeatures.decorator.test.ts @@ -5,8 +5,8 @@ */ import path from 'path'; -import { Table, AttributeType, BillingMode } from 'aws-cdk-lib/aws-dynamodb'; -import { App, Stack, RemovalPolicy } from 'aws-cdk-lib'; +import { AttributeType, BillingMode, Table } from 'aws-cdk-lib/aws-dynamodb'; +import { App, RemovalPolicy, Stack } from 'aws-cdk-lib'; import { XRayClient } from '@aws-sdk/client-xray'; import { STSClient } from '@aws-sdk/client-sts'; import { v4 } from 'uuid'; @@ -15,29 +15,29 @@ import { destroyStack, } from '../../../commons/tests/utils/cdk-cli'; import { - getTraces, - getInvocationSubsegment, - splitSegmentsByName, - invokeAllTestCases, createTracerTestFunction, - getFunctionArn, getFirstSubsegment, + getFunctionArn, + getInvocationSubsegment, + getTraces, + invokeAllTestCases, + splitSegmentsByName, } from '../helpers/tracesUtils'; import { generateUniqueName, isValidRuntimeKey, } from '../../../commons/tests/utils/e2eUtils'; import { - RESOURCE_NAME_PREFIX, - SETUP_TIMEOUT, - TEARDOWN_TIMEOUT, - TEST_CASE_TIMEOUT, expectedCustomAnnotationKey, expectedCustomAnnotationValue, + expectedCustomErrorMessage, expectedCustomMetadataKey, expectedCustomMetadataValue, expectedCustomResponseValue, - expectedCustomErrorMessage, + RESOURCE_NAME_PREFIX, + SETUP_TIMEOUT, + TEARDOWN_TIMEOUT, + TEST_CASE_TIMEOUT, } from './constants'; import { assertAnnotation, @@ -242,7 +242,7 @@ describe(`Tracer E2E tests, all features with decorator instantiation for runtim * 1. Lambda Context (AWS::Lambda) * 2. Lambda Function (AWS::Lambda::Function) * 4. DynamoDB (AWS::DynamoDB) - * 4. Remote call (awslabs.github.io) + * 4. Remote call (docs.powertools.aws.dev) */ expect(trace.Segments.length).toBe(4); const invocationSubsegment = getInvocationSubsegment(trace); @@ -251,7 +251,7 @@ describe(`Tracer E2E tests, all features with decorator instantiation for runtim * Invocation subsegment should have a subsegment '## index.handler' (default behavior for Tracer) * '## index.handler' subsegment should have 3 subsegments * 1. DynamoDB (PutItem on the table) - * 2. awslabs.github.io (Remote call) + * 2. docs.powertools.aws.dev (Remote call) * 3. '### myMethod' (method decorator) */ const handlerSubsegment = getFirstSubsegment(invocationSubsegment); @@ -263,11 +263,11 @@ describe(`Tracer E2E tests, all features with decorator instantiation for runtim } const subsegments = splitSegmentsByName(handlerSubsegment.subsegments, [ 'DynamoDB', - 'awslabs.github.io', + 'docs.powertools.aws.dev', '### myMethod', ]); expect(subsegments.get('DynamoDB')?.length).toBe(1); - expect(subsegments.get('awslabs.github.io')?.length).toBe(1); + expect(subsegments.get('docs.powertools.aws.dev')?.length).toBe(1); expect(subsegments.get('### myMethod')?.length).toBe(1); expect(subsegments.get('other')?.length).toBe(0); @@ -350,7 +350,7 @@ describe(`Tracer E2E tests, all features with decorator instantiation for runtim * 1. Lambda Context (AWS::Lambda) * 2. Lambda Function (AWS::Lambda::Function) * 3. DynamoDB (AWS::DynamoDB) - * 4. Remote call (awslabs.github.io) + * 4. Remote call (docs.powertools.aws.dev) */ expect(trace.Segments.length).toBe(4); const invocationSubsegment = getInvocationSubsegment(trace); @@ -359,7 +359,7 @@ describe(`Tracer E2E tests, all features with decorator instantiation for runtim * Invocation subsegment should have a subsegment '## index.handler' (default behavior for Tracer) * '## index.handler' subsegment should have 3 subsegments * 1. DynamoDB (PutItem on the table) - * 2. awslabs.github.io (Remote call) + * 2. docs.powertools.aws.dev (Remote call) * 3. '### myMethod' (method decorator) */ const handlerSubsegment = getFirstSubsegment(invocationSubsegment); @@ -371,11 +371,11 @@ describe(`Tracer E2E tests, all features with decorator instantiation for runtim } const subsegments = splitSegmentsByName(handlerSubsegment.subsegments, [ 'DynamoDB', - 'awslabs.github.io', + 'docs.powertools.aws.dev', '### myMethod', ]); expect(subsegments.get('DynamoDB')?.length).toBe(1); - expect(subsegments.get('awslabs.github.io')?.length).toBe(1); + expect(subsegments.get('docs.powertools.aws.dev')?.length).toBe(1); expect(subsegments.get('### myMethod')?.length).toBe(1); expect(subsegments.get('other')?.length).toBe(0); @@ -414,7 +414,7 @@ describe(`Tracer E2E tests, all features with decorator instantiation for runtim * 1. Lambda Context (AWS::Lambda) * 2. Lambda Function (AWS::Lambda::Function) * 3. DynamoDB (AWS::DynamoDB) - * 4. Remote call (awslabs.github.io) + * 4. Remote call (docs.powertools.aws.dev) */ expect(trace.Segments.length).toBe(4); const invocationSubsegment = getInvocationSubsegment(trace); @@ -423,7 +423,7 @@ describe(`Tracer E2E tests, all features with decorator instantiation for runtim * Invocation subsegment should have a subsegment '## index.handler' (default behavior for Tracer) * '## index.handler' subsegment should have 3 subsegments * 1. DynamoDB (PutItem on the table) - * 2. awslabs.github.io (Remote call) + * 2. docs.powertools.aws.dev (Remote call) * 3. '### myMethod' (method decorator) */ const handlerSubsegment = getFirstSubsegment(invocationSubsegment); @@ -439,11 +439,11 @@ describe(`Tracer E2E tests, all features with decorator instantiation for runtim } const subsegments = splitSegmentsByName(handlerSubsegment.subsegments, [ 'DynamoDB', - 'awslabs.github.io', + 'docs.powertools.aws.dev', '### myMethod', ]); expect(subsegments.get('DynamoDB')?.length).toBe(1); - expect(subsegments.get('awslabs.github.io')?.length).toBe(1); + expect(subsegments.get('docs.powertools.aws.dev')?.length).toBe(1); expect(subsegments.get('### myMethod')?.length).toBe(1); expect(subsegments.get('other')?.length).toBe(0); diff --git a/packages/tracer/tests/e2e/allFeatures.manual.test.ts b/packages/tracer/tests/e2e/allFeatures.manual.test.ts index 15f331c127..dbbebc34ea 100644 --- a/packages/tracer/tests/e2e/allFeatures.manual.test.ts +++ b/packages/tracer/tests/e2e/allFeatures.manual.test.ts @@ -5,8 +5,8 @@ */ import path from 'path'; -import { Table, AttributeType, BillingMode } from 'aws-cdk-lib/aws-dynamodb'; -import { App, Stack, RemovalPolicy } from 'aws-cdk-lib'; +import { AttributeType, BillingMode, Table } from 'aws-cdk-lib/aws-dynamodb'; +import { App, RemovalPolicy, Stack } from 'aws-cdk-lib'; import { XRayClient } from '@aws-sdk/client-xray'; import { STSClient } from '@aws-sdk/client-sts'; import { v4 } from 'uuid'; @@ -15,13 +15,13 @@ import { destroyStack, } from '../../../commons/tests/utils/cdk-cli'; import { - getTraces, - getInvocationSubsegment, - splitSegmentsByName, - invokeAllTestCases, createTracerTestFunction, - getFunctionArn, getFirstSubsegment, + getFunctionArn, + getInvocationSubsegment, + getTraces, + invokeAllTestCases, + splitSegmentsByName, } from '../helpers/tracesUtils'; import type { ParsedTrace } from '../helpers/traceUtils.types'; import { @@ -29,20 +29,20 @@ import { isValidRuntimeKey, } from '../../../commons/tests/utils/e2eUtils'; import { - RESOURCE_NAME_PREFIX, - SETUP_TIMEOUT, - TEARDOWN_TIMEOUT, - TEST_CASE_TIMEOUT, expectedCustomAnnotationKey, expectedCustomAnnotationValue, + expectedCustomErrorMessage, expectedCustomMetadataKey, expectedCustomMetadataValue, expectedCustomResponseValue, - expectedCustomErrorMessage, + RESOURCE_NAME_PREFIX, + SETUP_TIMEOUT, + TEARDOWN_TIMEOUT, + TEST_CASE_TIMEOUT, } from './constants'; import { - assertErrorAndFault, assertAnnotation, + assertErrorAndFault, } from '../helpers/traceAssertions'; const runtime: string = process.env.RUNTIME || 'nodejs18x'; @@ -146,7 +146,7 @@ describe(`Tracer E2E tests, all features with manual instantiation for runtime: * 1. Lambda Context (AWS::Lambda) * 2. Lambda Function (AWS::Lambda::Function) * 3. DynamoDB (AWS::DynamoDB) - * 4. Remote call (awslabs.github.io) + * 4. Remote call (docs.powertools.aws.dev) */ expect(trace.Segments.length).toBe(4); const invocationSubsegment = getInvocationSubsegment(trace); @@ -155,7 +155,7 @@ describe(`Tracer E2E tests, all features with manual instantiation for runtime: * Invocation subsegment should have a subsegment '## index.handler' (default behavior for Tracer) * '## index.handler' subsegment should have 2 subsegments * 1. DynamoDB (PutItem on the table) - * 2. awslabs.github.io (Remote call) + * 2. docs.powertools.aws.dev (Remote call) */ const handlerSubsegment = getFirstSubsegment(invocationSubsegment); expect(handlerSubsegment.name).toBe('## index.handler'); @@ -166,10 +166,10 @@ describe(`Tracer E2E tests, all features with manual instantiation for runtime: } const subsegments = splitSegmentsByName(handlerSubsegment.subsegments, [ 'DynamoDB', - 'awslabs.github.io', + 'docs.powertools.aws.dev', ]); expect(subsegments.get('DynamoDB')?.length).toBe(1); - expect(subsegments.get('awslabs.github.io')?.length).toBe(1); + expect(subsegments.get('docs.powertools.aws.dev')?.length).toBe(1); expect(subsegments.get('other')?.length).toBe(0); const shouldThrowAnError = i === invocations - 1; diff --git a/packages/tracer/tests/e2e/allFeatures.middy.test.ts b/packages/tracer/tests/e2e/allFeatures.middy.test.ts index 6cf3e24f44..69b612b604 100644 --- a/packages/tracer/tests/e2e/allFeatures.middy.test.ts +++ b/packages/tracer/tests/e2e/allFeatures.middy.test.ts @@ -5,8 +5,8 @@ */ import path from 'path'; -import { Table, AttributeType, BillingMode } from 'aws-cdk-lib/aws-dynamodb'; -import { App, Stack, RemovalPolicy } from 'aws-cdk-lib'; +import { AttributeType, BillingMode, Table } from 'aws-cdk-lib/aws-dynamodb'; +import { App, RemovalPolicy, Stack } from 'aws-cdk-lib'; import { XRayClient } from '@aws-sdk/client-xray'; import { STSClient } from '@aws-sdk/client-sts'; import { v4 } from 'uuid'; @@ -15,29 +15,29 @@ import { destroyStack, } from '../../../commons/tests/utils/cdk-cli'; import { - getTraces, - getInvocationSubsegment, - splitSegmentsByName, - invokeAllTestCases, createTracerTestFunction, - getFunctionArn, getFirstSubsegment, + getFunctionArn, + getInvocationSubsegment, + getTraces, + invokeAllTestCases, + splitSegmentsByName, } from '../helpers/tracesUtils'; import { generateUniqueName, isValidRuntimeKey, } from '../../../commons/tests/utils/e2eUtils'; import { - RESOURCE_NAME_PREFIX, - SETUP_TIMEOUT, - TEARDOWN_TIMEOUT, - TEST_CASE_TIMEOUT, expectedCustomAnnotationKey, expectedCustomAnnotationValue, + expectedCustomErrorMessage, expectedCustomMetadataKey, expectedCustomMetadataValue, expectedCustomResponseValue, - expectedCustomErrorMessage, + RESOURCE_NAME_PREFIX, + SETUP_TIMEOUT, + TEARDOWN_TIMEOUT, + TEST_CASE_TIMEOUT, } from './constants'; import { assertAnnotation, @@ -246,7 +246,7 @@ describe(`Tracer E2E tests, all features with middy instantiation for runtime: $ * 1. Lambda Context (AWS::Lambda) * 2. Lambda Function (AWS::Lambda::Function) * 3. DynamoDB Table (AWS::DynamoDB::Table) - * 4. Remote call (awslabs.github.io) + * 4. Remote call (docs.powertools.aws.dev) */ expect(trace.Segments.length).toBe(4); const invocationSubsegment = getInvocationSubsegment(trace); @@ -255,7 +255,7 @@ describe(`Tracer E2E tests, all features with middy instantiation for runtime: $ * Invocation subsegment should have a subsegment '## index.handler' (default behavior for Tracer) * '## index.handler' subsegment should have 2 subsegments * 1. DynamoDB (PutItem on the table) - * 2. awslabs.github.io (Remote call) + * 2. docs.powertools.aws.dev (Remote call) */ const handlerSubsegment = getFirstSubsegment(invocationSubsegment); expect(handlerSubsegment.name).toBe('## index.handler'); @@ -266,10 +266,10 @@ describe(`Tracer E2E tests, all features with middy instantiation for runtime: $ } const subsegments = splitSegmentsByName(handlerSubsegment.subsegments, [ 'DynamoDB', - 'awslabs.github.io', + 'docs.powertools.aws.dev', ]); expect(subsegments.get('DynamoDB')?.length).toBe(1); - expect(subsegments.get('awslabs.github.io')?.length).toBe(1); + expect(subsegments.get('docs.powertools.aws.dev')?.length).toBe(1); expect(subsegments.get('other')?.length).toBe(0); const shouldThrowAnError = i === invocations - 1; @@ -351,7 +351,7 @@ describe(`Tracer E2E tests, all features with middy instantiation for runtime: $ * 1. Lambda Context (AWS::Lambda) * 2. Lambda Function (AWS::Lambda::Function) * 3. DynamoDB Table (AWS::DynamoDB::Table) - * 4. Remote call (awslabs.github.io) + * 4. Remote call (docs.powertools.aws.dev) */ expect(trace.Segments.length).toBe(4); const invocationSubsegment = getInvocationSubsegment(trace); @@ -360,7 +360,7 @@ describe(`Tracer E2E tests, all features with middy instantiation for runtime: $ * Invocation subsegment should have a subsegment '## index.handler' (default behavior for Tracer) * '## index.handler' subsegment should have 2 subsegments * 1. DynamoDB (PutItem on the table) - * 2. awslabs.github.io (Remote call) + * 2. docs.powertools.aws.dev (Remote call) */ const handlerSubsegment = getFirstSubsegment(invocationSubsegment); expect(handlerSubsegment.name).toBe('## index.handler'); @@ -371,10 +371,10 @@ describe(`Tracer E2E tests, all features with middy instantiation for runtime: $ } const subsegments = splitSegmentsByName(handlerSubsegment.subsegments, [ 'DynamoDB', - 'awslabs.github.io', + 'docs.powertools.aws.dev', ]); expect(subsegments.get('DynamoDB')?.length).toBe(1); - expect(subsegments.get('awslabs.github.io')?.length).toBe(1); + expect(subsegments.get('docs.powertools.aws.dev')?.length).toBe(1); expect(subsegments.get('other')?.length).toBe(0); const shouldThrowAnError = i === invocations - 1; @@ -415,7 +415,7 @@ describe(`Tracer E2E tests, all features with middy instantiation for runtime: $ * 1. Lambda Context (AWS::Lambda) * 2. Lambda Function (AWS::Lambda::Function) * 3. DynamoDB Table (AWS::DynamoDB::Table) - * 4. Remote call (awslabs.github.io) + * 4. Remote call (docs.powertools.aws.dev) */ expect(trace.Segments.length).toBe(4); const invocationSubsegment = getInvocationSubsegment(trace); @@ -424,7 +424,7 @@ describe(`Tracer E2E tests, all features with middy instantiation for runtime: $ * Invocation subsegment should have a subsegment '## index.handlerWithNoCaptureResponseViaMiddlewareOption' (default behavior for Tracer) * '## index.handlerWithNoCaptureResponseViaMiddlewareOption' subsegment should have 2 subsegments * 1. DynamoDB (PutItem on the table) - * 2. awslabs.github.io (Remote call) + * 2. docs.powertools.aws.dev (Remote call) */ const handlerSubsegment = getFirstSubsegment(invocationSubsegment); expect(handlerSubsegment.name).toBe( @@ -439,10 +439,10 @@ describe(`Tracer E2E tests, all features with middy instantiation for runtime: $ } const subsegments = splitSegmentsByName(handlerSubsegment.subsegments, [ 'DynamoDB', - 'awslabs.github.io', + 'docs.powertools.aws.dev', ]); expect(subsegments.get('DynamoDB')?.length).toBe(1); - expect(subsegments.get('awslabs.github.io')?.length).toBe(1); + expect(subsegments.get('docs.powertools.aws.dev')?.length).toBe(1); expect(subsegments.get('other')?.length).toBe(0); const shouldThrowAnError = i === invocations - 1; diff --git a/packages/tracer/tests/e2e/asyncHandler.decorator.test.ts b/packages/tracer/tests/e2e/asyncHandler.decorator.test.ts index 2506e8a514..13693dfbdd 100644 --- a/packages/tracer/tests/e2e/asyncHandler.decorator.test.ts +++ b/packages/tracer/tests/e2e/asyncHandler.decorator.test.ts @@ -5,8 +5,8 @@ */ import path from 'path'; -import { Table, AttributeType, BillingMode } from 'aws-cdk-lib/aws-dynamodb'; -import { App, Stack, RemovalPolicy } from 'aws-cdk-lib'; +import { AttributeType, BillingMode, Table } from 'aws-cdk-lib/aws-dynamodb'; +import { App, RemovalPolicy, Stack } from 'aws-cdk-lib'; import { XRayClient } from '@aws-sdk/client-xray'; import { STSClient } from '@aws-sdk/client-sts'; import { v4 } from 'uuid'; @@ -15,30 +15,30 @@ import { destroyStack, } from '../../../commons/tests/utils/cdk-cli'; import { - getTraces, - getInvocationSubsegment, - splitSegmentsByName, - invokeAllTestCases, createTracerTestFunction, - getFunctionArn, getFirstSubsegment, + getFunctionArn, + getInvocationSubsegment, + getTraces, + invokeAllTestCases, + splitSegmentsByName, } from '../helpers/tracesUtils'; import { generateUniqueName, isValidRuntimeKey, } from '../../../commons/tests/utils/e2eUtils'; import { - RESOURCE_NAME_PREFIX, - SETUP_TIMEOUT, - TEARDOWN_TIMEOUT, - TEST_CASE_TIMEOUT, - expectedCustomErrorMessage, expectedCustomAnnotationKey, expectedCustomAnnotationValue, + expectedCustomErrorMessage, expectedCustomMetadataKey, expectedCustomMetadataValue, expectedCustomResponseValue, expectedCustomSubSegmentName, + RESOURCE_NAME_PREFIX, + SETUP_TIMEOUT, + TEARDOWN_TIMEOUT, + TEST_CASE_TIMEOUT, } from './constants'; import { assertAnnotation, @@ -179,7 +179,7 @@ describe(`Tracer E2E tests, asynchronous handler with decorator instantiation fo * 1. Lambda Context (AWS::Lambda) * 2. Lambda Function (AWS::Lambda::Function) * 3. DynamoDB Table (AWS::DynamoDB::Table) - * 4. Remote call (awslabs.github.io) + * 4. Remote call (docs.powertools.aws.dev) */ expect(trace.Segments.length).toBe(4); const invocationSubsegment = getInvocationSubsegment(trace); @@ -188,7 +188,7 @@ describe(`Tracer E2E tests, asynchronous handler with decorator instantiation fo * Invocation subsegment should have a subsegment '## index.handler' (default behavior for Tracer) * '## index.handler' subsegment should have 3 subsegments * 1. DynamoDB (PutItem on the table) - * 2. awslabs.github.io (Remote call) + * 2. docs.powertools.aws.dev (Remote call) * 3. '### myMethod' (method decorator) */ const handlerSubsegment = getFirstSubsegment(invocationSubsegment); @@ -200,11 +200,11 @@ describe(`Tracer E2E tests, asynchronous handler with decorator instantiation fo } const subsegments = splitSegmentsByName(handlerSubsegment.subsegments, [ 'DynamoDB', - 'awslabs.github.io', + 'docs.powertools.aws.dev', '### myMethod', ]); expect(subsegments.get('DynamoDB')?.length).toBe(1); - expect(subsegments.get('awslabs.github.io')?.length).toBe(1); + expect(subsegments.get('docs.powertools.aws.dev')?.length).toBe(1); expect(subsegments.get('### myMethod')?.length).toBe(1); expect(subsegments.get('other')?.length).toBe(0); @@ -287,7 +287,7 @@ describe(`Tracer E2E tests, asynchronous handler with decorator instantiation fo * 1. Lambda Context (AWS::Lambda) * 2. Lambda Function (AWS::Lambda::Function) * 3. DynamoDB Table (AWS::DynamoDB::Table) - * 4. Remote call (awslabs.github.io) + * 4. Remote call (docs.powertools.aws.dev) */ expect(trace.Segments.length).toBe(4); const invocationSubsegment = getInvocationSubsegment(trace); @@ -296,7 +296,7 @@ describe(`Tracer E2E tests, asynchronous handler with decorator instantiation fo * Invocation subsegment should have a subsegment '## index.handler' (default behavior for Tracer) * '## index.handler' subsegment should have 3 subsegments * 1. DynamoDB (PutItem on the table) - * 2. awslabs.github.io (Remote call) + * 2. docs.powertools.aws.dev (Remote call) * 3. '### mySubsegment' (method decorator with custom name) */ const handlerSubsegment = getFirstSubsegment(invocationSubsegment); @@ -310,11 +310,11 @@ describe(`Tracer E2E tests, asynchronous handler with decorator instantiation fo } const subsegments = splitSegmentsByName(handlerSubsegment.subsegments, [ 'DynamoDB', - 'awslabs.github.io', + 'docs.powertools.aws.dev', expectedCustomSubSegmentName, ]); expect(subsegments.get('DynamoDB')?.length).toBe(1); - expect(subsegments.get('awslabs.github.io')?.length).toBe(1); + expect(subsegments.get('docs.powertools.aws.dev')?.length).toBe(1); expect(subsegments.get(expectedCustomSubSegmentName)?.length).toBe(1); expect(subsegments.get('other')?.length).toBe(0); diff --git a/packages/tracer/tests/helpers/tracesUtils.ts b/packages/tracer/tests/helpers/tracesUtils.ts index b742a1f9b7..3bda9c551c 100644 --- a/packages/tracer/tests/helpers/tracesUtils.ts +++ b/packages/tracer/tests/helpers/tracesUtils.ts @@ -2,25 +2,25 @@ import promiseRetry from 'promise-retry'; import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs'; import { Duration } from 'aws-cdk-lib'; import { Architecture, Tracing } from 'aws-cdk-lib/aws-lambda'; +import type { XRayClient } from '@aws-sdk/client-xray'; import { - GetTraceSummariesCommand, BatchGetTracesCommand, + GetTraceSummariesCommand, } from '@aws-sdk/client-xray'; -import type { XRayClient } from '@aws-sdk/client-xray'; import type { STSClient } from '@aws-sdk/client-sts'; import { GetCallerIdentityCommand } from '@aws-sdk/client-sts'; import { expectedCustomAnnotationKey, expectedCustomAnnotationValue, + expectedCustomErrorMessage, expectedCustomMetadataKey, expectedCustomMetadataValue, expectedCustomResponseValue, - expectedCustomErrorMessage, } from '../e2e/constants'; import { invokeFunction, - TestRuntimesKey, TEST_RUNTIMES, + TestRuntimesKey, } from '../../../commons/tests/utils/e2eUtils'; import { FunctionSegmentNotDefinedError } from './FunctionSegmentNotDefinedError'; import type {