diff --git a/packages/commons/src/utils/lambda/LambdaInterface.ts b/packages/commons/src/utils/lambda/LambdaInterface.ts index 03de08b1fb..957ab1749c 100644 --- a/packages/commons/src/utils/lambda/LambdaInterface.ts +++ b/packages/commons/src/utils/lambda/LambdaInterface.ts @@ -1,9 +1,20 @@ import { Handler } from 'aws-lambda'; +export type SyncHandler = ( + event: Parameters[0], + context: Parameters[1], + callback: Parameters[2], +) => void; + +export type AsyncHandler = ( + event: Parameters[0], + context: Parameters[1], +) => Promise[2]>[1]>>; + interface LambdaInterface { - handler: Handler + handler: SyncHandler | AsyncHandler } export { - LambdaInterface, + LambdaInterface }; \ No newline at end of file diff --git a/packages/commons/tests/unit/LambdaInterface.test.ts b/packages/commons/tests/unit/LambdaInterface.test.ts index 853188a7c4..272961a913 100644 --- a/packages/commons/tests/unit/LambdaInterface.test.ts +++ b/packages/commons/tests/unit/LambdaInterface.test.ts @@ -1,23 +1,128 @@ +import { Handler } from 'aws-lambda'; import { Callback, Context } from 'aws-lambda'; -import { ContextExamples, LambdaInterface } from '../../src'; +import { ContextExamples, SyncHandler, AsyncHandler, LambdaInterface } from '../../src'; -describe('LambdaInterface', () => { - test('it compiles', async () => { +describe('LambdaInterface with arrow function', () => { + test('it compiles when given a callback', async () => { class LambdaFunction implements LambdaInterface { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - public handler( - _event: TEvent, - context: Context, - _callback: Callback, - ): void | Promise { + + public handler: SyncHandler = async (_event: unknown, context: Context, _callback: Callback) => { context.done(); context.fail(new Error('test Error')); context.succeed('test succeed'); context.getRemainingTimeInMillis(); + _callback(null, 'Hello World'); + }; + } + + await new LambdaFunction().handler({}, ContextExamples.helloworldContext, () => console.log('Lambda invoked!')); + }); + + test('it compiles when not given a callback', async () => { + class LambdaFunction implements LambdaInterface { + + public handler: AsyncHandler = async (_event: unknown, context: Context) => { + context.getRemainingTimeInMillis(); + }; + } + + await new LambdaFunction().handler({}, ContextExamples.helloworldContext); + }); +}); + +describe('LambdaInterface with standard function', () => { + test('it compiles when given a callback', async () => { + class LambdaFunction implements LambdaInterface { + + public handler(_event: unknown, context: Context, _callback: Callback): void { + context.getRemainingTimeInMillis(); + _callback(null, 'Hello World'); } } await new LambdaFunction().handler({}, ContextExamples.helloworldContext, () => console.log('Lambda invoked!')); }); + + test('it compiles when not given a callback', async () => { + class LambdaFunction implements LambdaInterface { + + public async handler (_event: unknown, context: Context): Promise { + context.getRemainingTimeInMillis(); + + return new Promise((resolve) => { + resolve('test promise'); + }); + } + } + + await new LambdaFunction().handler({}, ContextExamples.helloworldContext); + }); + +}); + +describe('LambdaInterface with decorator', () => { + type HandlerMethodDecorator = ( + target: LambdaInterface, + propertyKey: string | symbol, + descriptor: TypedPropertyDescriptor> | TypedPropertyDescriptor> + ) => void; + + class DummyModule { + + public dummyDecorator(): HandlerMethodDecorator { + return (target, _propertyKey, descriptor) => { + const originalMethod = descriptor.value; + + descriptor.value = ( async (event, context, callback) => { + + let result: unknown; + try { + console.log(`Invoking ${String(_propertyKey)}`); + result = await originalMethod?.apply(this, [ event, context, callback ]); + console.log(`Invoked ${String(_propertyKey)}`); + } catch (error) { + throw error; + } finally { + console.log(`Finally from decorator`); + } + + return result; + }); + + return descriptor; + }; + } + } + + const dummyModule = new DummyModule(); + + test('decorator without callback compile', async () => { + + // WHEN + class LambdaFunction implements LambdaInterface { + + @dummyModule.dummyDecorator() + public async handler(_event: unknown, context: Context): Promise { + context.getRemainingTimeInMillis(); + + return 'test'; + } + } + + await new LambdaFunction().handler({}, ContextExamples.helloworldContext); + }); + + test('decorator with callback compile', async () => { + + // WHEN + class LambdaFunction implements LambdaInterface { + + @dummyModule.dummyDecorator() + public handler(_event: unknown, context: Context, _callback: Callback): void { + context.getRemainingTimeInMillis(); + } + } + + await new LambdaFunction().handler({}, ContextExamples.helloworldContext, () => console.log('Lambda invoked!')); + }); });