From 4ee57bd3deb9b33accc1cb973b08b4641e68fc93 Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Thu, 12 Oct 2023 11:59:21 +0200 Subject: [PATCH] feat(batch): add esmodule support (#1737) --- .../batch/{jest.config.js => jest.config.cjs} | 3 + packages/batch/package.json | 35 ++++++++- .../batch/src/BasePartialBatchProcessor.ts | 12 ++- packages/batch/src/BasePartialProcessor.ts | 2 +- packages/batch/src/BatchProcessor.ts | 6 +- packages/batch/src/BatchProcessorSync.ts | 6 +- packages/batch/src/SqsFifoPartialProcessor.ts | 8 +- packages/batch/src/constants.ts | 2 +- packages/batch/src/errors.ts | 2 +- packages/batch/src/index.ts | 23 +++--- packages/batch/src/processPartialResponse.ts | 6 +- .../batch/src/processPartialResponseSync.ts | 6 +- packages/batch/tests/helpers/handlers.ts | 2 +- .../tests/unit/BasePartialProcessor.test.ts | 74 +++++++++++++++++++ .../batch/tests/unit/BatchProcessor.test.ts | 19 +++-- .../tests/unit/BatchProcessorSync.test.ts | 19 +++-- .../unit/SqsFifoPartialProcessor.test.ts | 6 +- .../tests/unit/processPartialResponse.test.ts | 36 +++++---- .../unit/processPartialResponseSync.test.ts | 36 +++++---- packages/batch/tsconfig.esm.json | 11 +++ packages/batch/tsconfig.json | 2 +- 21 files changed, 230 insertions(+), 86 deletions(-) rename packages/batch/{jest.config.js => jest.config.cjs} (93%) create mode 100644 packages/batch/tests/unit/BasePartialProcessor.test.ts create mode 100644 packages/batch/tsconfig.esm.json diff --git a/packages/batch/jest.config.js b/packages/batch/jest.config.cjs similarity index 93% rename from packages/batch/jest.config.js rename to packages/batch/jest.config.cjs index 3db7c7a6da..9eaa88fbf4 100644 --- a/packages/batch/jest.config.js +++ b/packages/batch/jest.config.cjs @@ -5,6 +5,9 @@ module.exports = { }, runner: 'groups', preset: 'ts-jest', + moduleNameMapper: { + '^(\\.{1,2}/.*)\\.js$': '$1', + }, transform: { '^.+\\.ts?$': 'ts-jest', }, diff --git a/packages/batch/package.json b/packages/batch/package.json index 98f7b8e5c8..c71e2fd86d 100644 --- a/packages/batch/package.json +++ b/packages/batch/package.json @@ -19,19 +19,46 @@ "test:e2e:nodejs20x": "echo 'Not Implemented'", "test:e2e": "echo 'Not Implemented'", "watch": "jest --watch", - "build": "tsc --build --force", + "build:cjs": "tsc --build --force && echo '{ \"type\": \"commonjs\" }' > lib/cjs/package.json", + "build:esm": "tsc --project tsconfig.esm.json && echo '{ \"type\": \"module\" }' > lib/esm/package.json", + "build": "npm run build:esm & npm run build:cjs", "lint": "eslint --ext .ts,.js --no-error-on-unmatched-pattern .", "lint-fix": "eslint --fix --ext .ts,.js --no-error-on-unmatched-pattern .", "prebuild": "rimraf ./lib", - "prepack": "node ../../.github/scripts/release_patch_package_json.js ." + "prepack": "rimraf ./lib/*.tsbuildinfo && node ../../.github/scripts/release_patch_package_json.js ." }, "lint-staged": { "*.{js,ts}": "npm run lint-fix" }, "homepage": "https://github.com/aws-powertools/powertools-lambda-typescript/tree/main/packages/batch#readme", "license": "MIT-0", - "main": "./lib/index.js", - "types": "./lib/index.d.ts", + "type": "module", + "exports": { + ".": { + "require": { + "types": "./lib/cjs/index.d.ts", + "default": "./lib/cjs/index.js" + }, + "import": { + "types": "./lib/esm/index.d.ts", + "default": "./lib/esm/index.js" + } + }, + "./types": { + "import": "./lib/esm/types.js", + "require": "./lib/cjs/types.js" + } + }, + "typesVersions": { + "*": { + "types": [ + "lib/cjs/types.d.ts", + "lib/esm/types.d.ts" + ] + } + }, + "types": "./lib/cjs/index.d.ts", + "main": "./lib/cjs/index.js", "files": [ "lib" ], diff --git a/packages/batch/src/BasePartialBatchProcessor.ts b/packages/batch/src/BasePartialBatchProcessor.ts index 3cf4b30309..10b04b3e4e 100644 --- a/packages/batch/src/BasePartialBatchProcessor.ts +++ b/packages/batch/src/BasePartialBatchProcessor.ts @@ -3,14 +3,18 @@ import type { KinesisStreamRecord, SQSRecord, } from 'aws-lambda'; -import { BasePartialProcessor } from './BasePartialProcessor'; -import { DATA_CLASS_MAPPING, DEFAULT_RESPONSE, EventType } from './constants'; -import { FullBatchFailureError } from './errors'; +import { BasePartialProcessor } from './BasePartialProcessor.js'; +import { + DATA_CLASS_MAPPING, + DEFAULT_RESPONSE, + EventType, +} from './constants.js'; +import { FullBatchFailureError } from './errors.js'; import type { EventSourceDataClassTypes, PartialItemFailureResponse, PartialItemFailures, -} from './types'; +} from './types.js'; /** * Process batch and partially report failed items diff --git a/packages/batch/src/BasePartialProcessor.ts b/packages/batch/src/BasePartialProcessor.ts index 9de6901394..f4ec911446 100644 --- a/packages/batch/src/BasePartialProcessor.ts +++ b/packages/batch/src/BasePartialProcessor.ts @@ -5,7 +5,7 @@ import type { FailureResponse, ResultType, SuccessResponse, -} from './types'; +} from './types.js'; /** * Abstract class for batch processors. diff --git a/packages/batch/src/BatchProcessor.ts b/packages/batch/src/BatchProcessor.ts index bbe9856f07..960bcd4dde 100644 --- a/packages/batch/src/BatchProcessor.ts +++ b/packages/batch/src/BatchProcessor.ts @@ -1,6 +1,6 @@ -import { BasePartialBatchProcessor } from './BasePartialBatchProcessor'; -import { BatchProcessingError } from './errors'; -import type { BaseRecord, FailureResponse, SuccessResponse } from './types'; +import { BasePartialBatchProcessor } from './BasePartialBatchProcessor.js'; +import { BatchProcessingError } from './errors.js'; +import type { BaseRecord, FailureResponse, SuccessResponse } from './types.js'; /** * Process native partial responses from SQS, Kinesis Data Streams, and DynamoDB diff --git a/packages/batch/src/BatchProcessorSync.ts b/packages/batch/src/BatchProcessorSync.ts index 5bb8ce1f12..87a1566f4c 100644 --- a/packages/batch/src/BatchProcessorSync.ts +++ b/packages/batch/src/BatchProcessorSync.ts @@ -1,6 +1,6 @@ -import { BasePartialBatchProcessor } from './BasePartialBatchProcessor'; -import { BatchProcessingError } from './errors'; -import type { BaseRecord, FailureResponse, SuccessResponse } from './types'; +import { BasePartialBatchProcessor } from './BasePartialBatchProcessor.js'; +import { BatchProcessingError } from './errors.js'; +import type { BaseRecord, FailureResponse, SuccessResponse } from './types.js'; /** * Process native partial responses from SQS, Kinesis Data Streams, and DynamoDB diff --git a/packages/batch/src/SqsFifoPartialProcessor.ts b/packages/batch/src/SqsFifoPartialProcessor.ts index 2aca4ed814..3189c00fd5 100644 --- a/packages/batch/src/SqsFifoPartialProcessor.ts +++ b/packages/batch/src/SqsFifoPartialProcessor.ts @@ -1,7 +1,7 @@ -import { BatchProcessorSync } from './BatchProcessorSync'; -import { EventType } from './constants'; -import { SqsFifoShortCircuitError } from './errors'; -import type { FailureResponse, SuccessResponse } from './types'; +import { BatchProcessorSync } from './BatchProcessorSync.js'; +import { EventType } from './constants.js'; +import { SqsFifoShortCircuitError } from './errors.js'; +import type { FailureResponse, SuccessResponse } from './types.js'; /** * Process native partial responses from SQS FIFO queues diff --git a/packages/batch/src/constants.ts b/packages/batch/src/constants.ts index d06d5a8872..5f55e1e347 100644 --- a/packages/batch/src/constants.ts +++ b/packages/batch/src/constants.ts @@ -6,7 +6,7 @@ import type { import type { PartialItemFailureResponse, EventSourceDataClassTypes, -} from './types'; +} from './types.js'; const EventType = { SQS: 'SQS', diff --git a/packages/batch/src/errors.ts b/packages/batch/src/errors.ts index 8d2b0327b0..4ce6b27de9 100644 --- a/packages/batch/src/errors.ts +++ b/packages/batch/src/errors.ts @@ -1,4 +1,4 @@ -import { EventType } from './constants'; +import { EventType } from './constants.js'; /** * Base error thrown by the Batch Processing utility diff --git a/packages/batch/src/index.ts b/packages/batch/src/index.ts index b1b1069b26..6613712b7e 100644 --- a/packages/batch/src/index.ts +++ b/packages/batch/src/index.ts @@ -1,10 +1,13 @@ -export * from './constants'; -export * from './errors'; -export * from './types'; -export * from './BasePartialProcessor'; -export * from './BasePartialBatchProcessor'; -export * from './BatchProcessorSync'; -export * from './BatchProcessor'; -export * from './processPartialResponseSync'; -export * from './processPartialResponse'; -export * from './SqsFifoPartialProcessor'; +export { EventType } from './constants.js'; +export { + BatchProcessingError, + FullBatchFailureError, + SqsFifoShortCircuitError, + UnexpectedBatchTypeError, +} from './errors.js'; +export { BasePartialBatchProcessor } from './BasePartialBatchProcessor.js'; +export { BatchProcessorSync } from './BatchProcessorSync.js'; +export { BatchProcessor } from './BatchProcessor.js'; +export { processPartialResponseSync } from './processPartialResponseSync.js'; +export { processPartialResponse } from './processPartialResponse.js'; +export { SqsFifoPartialProcessor } from './SqsFifoPartialProcessor.js'; diff --git a/packages/batch/src/processPartialResponse.ts b/packages/batch/src/processPartialResponse.ts index 947b4268b6..8c5d7d79c9 100644 --- a/packages/batch/src/processPartialResponse.ts +++ b/packages/batch/src/processPartialResponse.ts @@ -1,10 +1,10 @@ -import { BasePartialBatchProcessor } from './BasePartialBatchProcessor'; -import { UnexpectedBatchTypeError } from './errors'; +import { BasePartialBatchProcessor } from './BasePartialBatchProcessor.js'; +import { UnexpectedBatchTypeError } from './errors.js'; import type { BaseRecord, BatchProcessingOptions, PartialItemFailureResponse, -} from './types'; +} from './types.js'; /** * Higher level function to handle batch event processing diff --git a/packages/batch/src/processPartialResponseSync.ts b/packages/batch/src/processPartialResponseSync.ts index 474713e7d5..7fb7056385 100644 --- a/packages/batch/src/processPartialResponseSync.ts +++ b/packages/batch/src/processPartialResponseSync.ts @@ -1,10 +1,10 @@ -import { BasePartialBatchProcessor } from './BasePartialBatchProcessor'; -import { UnexpectedBatchTypeError } from './errors'; +import { BasePartialBatchProcessor } from './BasePartialBatchProcessor.js'; +import { UnexpectedBatchTypeError } from './errors.js'; import type { BaseRecord, BatchProcessingOptions, PartialItemFailureResponse, -} from './types'; +} from './types.js'; /** * Higher level function to handle batch event processing diff --git a/packages/batch/tests/helpers/handlers.ts b/packages/batch/tests/helpers/handlers.ts index 0256129f9b..d8f9a638a0 100644 --- a/packages/batch/tests/helpers/handlers.ts +++ b/packages/batch/tests/helpers/handlers.ts @@ -2,8 +2,8 @@ import type { DynamoDBRecord, KinesisStreamRecord, SQSRecord, + Context, } from 'aws-lambda'; -import type { Context } from 'aws-lambda'; const sqsRecordHandler = (record: SQSRecord): string => { const body = record.body; diff --git a/packages/batch/tests/unit/BasePartialProcessor.test.ts b/packages/batch/tests/unit/BasePartialProcessor.test.ts new file mode 100644 index 0000000000..aa3bdb3d4b --- /dev/null +++ b/packages/batch/tests/unit/BasePartialProcessor.test.ts @@ -0,0 +1,74 @@ +/** + * Test BasePartialBatchProcessor class + * + * @group unit/batch/class/basepartialbatchprocessor + */ +import { BasePartialBatchProcessor, EventType } from '../../src/index.js'; +import type { + BaseRecord, + FailureResponse, + SuccessResponse, +} from '../../src/types.js'; +import { sqsRecordFactory } from '../helpers/factories.js'; +import { sqsRecordHandler } from '../helpers/handlers.js'; + +describe('Class: BasePartialBatchProcessor', () => { + const ENVIRONMENT_VARIABLES = process.env; + + beforeEach(() => { + jest.clearAllMocks(); + jest.resetModules(); + process.env = { ...ENVIRONMENT_VARIABLES }; + }); + + afterAll(() => { + process.env = ENVIRONMENT_VARIABLES; + }); + + class MyPartialProcessor extends BasePartialBatchProcessor { + public constructor() { + super(EventType.SQS); + } + + public async processRecord( + _record: BaseRecord + ): Promise { + throw new Error('Not implemented'); + } + + public processRecordSync( + record: BaseRecord + ): SuccessResponse | FailureResponse { + console.log('Processing record'); + + return this.successHandler(record, 'success'); + } + } + + describe('create custom batch partial processor', () => { + it('should create a custom batch partial processor', () => { + // Act + const processor = new MyPartialProcessor(); + + // Assess + expect(processor).toBeInstanceOf(BasePartialBatchProcessor); + }); + + it('should process a batch of records', () => { + // Prepare + const processor = new MyPartialProcessor(); + const records = [sqsRecordFactory('success')]; + const consoleSpy = jest.spyOn(console, 'log'); + + // Act + processor.register(records, sqsRecordHandler); + const processedMessages = processor.processSync(); + + // Assess + expect(processedMessages).toStrictEqual([ + ['success', records[0].body, records[0]], + ]); + expect(consoleSpy).toHaveBeenCalledTimes(1); + }); + }); +}); diff --git a/packages/batch/tests/unit/BatchProcessor.test.ts b/packages/batch/tests/unit/BatchProcessor.test.ts index 3243c1a4a0..4588e62099 100644 --- a/packages/batch/tests/unit/BatchProcessor.test.ts +++ b/packages/batch/tests/unit/BatchProcessor.test.ts @@ -1,25 +1,28 @@ /** - * Test AsyncBatchProcessor class + * Test BatchProcessor class * - * @group unit/batch/class/asyncBatchProcessor + * @group unit/batch/class/batchprocessor */ import type { Context } from 'aws-lambda'; import { ContextExamples as dummyContext } from '@aws-lambda-powertools/commons'; -import { BatchProcessor } from '../../src/BatchProcessor'; -import { EventType } from '../../src/constants'; -import { BatchProcessingError, FullBatchFailureError } from '../../src/errors'; -import type { BatchProcessingOptions } from '../../src/types'; +import { + BatchProcessor, + EventType, + BatchProcessingError, + FullBatchFailureError, +} from '../../src/index.js'; +import type { BatchProcessingOptions } from '../../src/types.js'; import { dynamodbRecordFactory, kinesisRecordFactory, sqsRecordFactory, -} from '../helpers/factories'; +} from '../helpers/factories.js'; import { asyncDynamodbRecordHandler, asyncKinesisRecordHandler, asyncSqsRecordHandler, asyncHandlerWithContext, -} from '../helpers/handlers'; +} from '../helpers/handlers.js'; describe('Class: AsyncBatchProcessor', () => { const ENVIRONMENT_VARIABLES = process.env; diff --git a/packages/batch/tests/unit/BatchProcessorSync.test.ts b/packages/batch/tests/unit/BatchProcessorSync.test.ts index 506e192d02..31a5315431 100644 --- a/packages/batch/tests/unit/BatchProcessorSync.test.ts +++ b/packages/batch/tests/unit/BatchProcessorSync.test.ts @@ -1,25 +1,28 @@ /** - * Test BatchProcessor class + * Test BatchProcessorSync class * - * @group unit/batch/class/batchprocessor + * @group unit/batch/class/batchprocessorsync */ import type { Context } from 'aws-lambda'; import { ContextExamples as dummyContext } from '@aws-lambda-powertools/commons'; -import { BatchProcessorSync } from '../../src/BatchProcessorSync'; -import { EventType } from '../../src/constants'; -import { BatchProcessingError, FullBatchFailureError } from '../../src/errors'; -import type { BatchProcessingOptions } from '../../src/types'; +import { + BatchProcessorSync, + EventType, + BatchProcessingError, + FullBatchFailureError, +} from '../../src/index.js'; +import type { BatchProcessingOptions } from '../../src/types.js'; import { dynamodbRecordFactory, kinesisRecordFactory, sqsRecordFactory, -} from '../helpers/factories'; +} from '../helpers/factories.js'; import { dynamodbRecordHandler, handlerWithContext, kinesisRecordHandler, sqsRecordHandler, -} from '../helpers/handlers'; +} from '../helpers/handlers.js'; describe('Class: BatchProcessor', () => { const ENVIRONMENT_VARIABLES = process.env; diff --git a/packages/batch/tests/unit/SqsFifoPartialProcessor.test.ts b/packages/batch/tests/unit/SqsFifoPartialProcessor.test.ts index 5c7f6c5796..61d12183fc 100644 --- a/packages/batch/tests/unit/SqsFifoPartialProcessor.test.ts +++ b/packages/batch/tests/unit/SqsFifoPartialProcessor.test.ts @@ -7,9 +7,9 @@ import { SqsFifoPartialProcessor, processPartialResponseSync, SqsFifoShortCircuitError, -} from '../../src'; -import { sqsRecordFactory } from '../helpers/factories'; -import { sqsRecordHandler } from '../helpers/handlers'; +} from '../../src/index.js'; +import { sqsRecordFactory } from '../helpers/factories.js'; +import { sqsRecordHandler } from '../helpers/handlers.js'; describe('Class: SqsFifoBatchProcessor', () => { const ENVIRONMENT_VARIABLES = process.env; diff --git a/packages/batch/tests/unit/processPartialResponse.test.ts b/packages/batch/tests/unit/processPartialResponse.test.ts index d344eb7883..66d97177a5 100644 --- a/packages/batch/tests/unit/processPartialResponse.test.ts +++ b/packages/batch/tests/unit/processPartialResponse.test.ts @@ -13,24 +13,28 @@ import { ContextExamples as dummyContext, Events as dummyEvent, } from '@aws-lambda-powertools/commons'; -import { BatchProcessor } from '../../src/BatchProcessor'; -import { processPartialResponse } from '../../src/processPartialResponse'; -import { EventType } from '../../src/constants'; +import { + BatchProcessor, + processPartialResponse, + EventType, + UnexpectedBatchTypeError, +} from '../../src/index.js'; import type { BatchProcessingOptions, PartialItemFailureResponse, -} from '../../src/types'; +} from '../../src/types.js'; import { dynamodbRecordFactory, kinesisRecordFactory, sqsRecordFactory, -} from '../helpers/factories'; +} from '../helpers/factories.js'; import { asyncDynamodbRecordHandler, asyncHandlerWithContext, asyncKinesisRecordHandler, asyncSqsRecordHandler, -} from '../helpers/handlers'; +} from '../helpers/handlers.js'; +import assert from 'node:assert'; describe('Function: processPartialResponse()', () => { const ENVIRONMENT_VARIABLES = process.env; @@ -186,14 +190,18 @@ describe('Function: processPartialResponse()', () => { ); }; - // Act & Assess - await expect(() => - handler(event as unknown as SQSEvent, context.helloworldContext) - ).rejects.toThrowError( - `Unexpected batch type. Possible values are: ${Object.keys( - EventType - ).join(', ')}` - ); + try { + // Act + await handler(event as unknown as SQSEvent, context.helloworldContext); + } catch (error) { + // Assess + assert(error instanceof UnexpectedBatchTypeError); + expect(error.message).toBe( + `Unexpected batch type. Possible values are: ${Object.keys( + EventType + ).join(', ')}` + ); + } }); test('Process partial response through handler with context provided', async () => { diff --git a/packages/batch/tests/unit/processPartialResponseSync.test.ts b/packages/batch/tests/unit/processPartialResponseSync.test.ts index 426428f5d4..e6700d8959 100644 --- a/packages/batch/tests/unit/processPartialResponseSync.test.ts +++ b/packages/batch/tests/unit/processPartialResponseSync.test.ts @@ -13,24 +13,28 @@ import { ContextExamples as dummyContext, Events as dummyEvent, } from '@aws-lambda-powertools/commons'; -import { BatchProcessorSync } from '../../src/BatchProcessorSync'; -import { processPartialResponseSync } from '../../src/processPartialResponseSync'; -import { EventType } from '../../src/constants'; +import { + BatchProcessorSync, + processPartialResponseSync, + EventType, + UnexpectedBatchTypeError, +} from '../../src/index.js'; import type { BatchProcessingOptions, PartialItemFailureResponse, -} from '../../src/types'; +} from '../../src/types.js'; import { dynamodbRecordFactory, kinesisRecordFactory, sqsRecordFactory, -} from '../helpers/factories'; +} from '../helpers/factories.js'; import { dynamodbRecordHandler, handlerWithContext, kinesisRecordHandler, sqsRecordHandler, -} from '../helpers/handlers'; +} from '../helpers/handlers.js'; +import assert from 'node:assert'; describe('Function: processPartialResponse()', () => { const ENVIRONMENT_VARIABLES = process.env; @@ -182,14 +186,18 @@ describe('Function: processPartialResponse()', () => { return processPartialResponseSync(event, sqsRecordHandler, processor); }; - // Act & Assess - expect(() => - handler(event as unknown as SQSEvent, context.helloworldContext) - ).toThrowError( - `Unexpected batch type. Possible values are: ${Object.keys( - EventType - ).join(', ')}` - ); + try { + // Act + handler(event as unknown as SQSEvent, context.helloworldContext); + } catch (error) { + // Assess + assert(error instanceof UnexpectedBatchTypeError); + expect(error.message).toBe( + `Unexpected batch type. Possible values are: ${Object.keys( + EventType + ).join(', ')}` + ); + } }); test('Process partial response through handler with context provided', () => { diff --git a/packages/batch/tsconfig.esm.json b/packages/batch/tsconfig.esm.json new file mode 100644 index 0000000000..9bed8e4da4 --- /dev/null +++ b/packages/batch/tsconfig.esm.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.esm.json", + "compilerOptions": { + "baseUrl": ".", + "outDir": "./lib/esm", + "rootDir": "./src" + }, + "include": [ + "./src/**/*" + ] +} \ No newline at end of file diff --git a/packages/batch/tsconfig.json b/packages/batch/tsconfig.json index 1cb9d72773..5be842b9f8 100644 --- a/packages/batch/tsconfig.json +++ b/packages/batch/tsconfig.json @@ -1,7 +1,7 @@ { "extends": "../../tsconfig.json", "compilerOptions": { - "outDir": "./lib", + "outDir": "./lib/cjs/", "rootDir": "./src", }, "include": [