-
Notifications
You must be signed in to change notification settings - Fork 146
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(commons): add
cleanupPowertools
function (#1473)
* feat: add cleanupPowertools hook * tests: fixed tests * chore: rename cleanup function
- Loading branch information
1 parent
d4ae762
commit 5bd0166
Showing
5 changed files
with
173 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
import { | ||
TRACER_KEY, | ||
METRICS_KEY, | ||
LOGGER_KEY, | ||
IDEMPOTENCY_KEY, | ||
} from './constants'; | ||
import type { MiddyLikeRequest, CleanupFunction } from '../types/middy'; | ||
|
||
// Typeguard to assert that an object is of Function type | ||
const isFunction = (obj: unknown): obj is CleanupFunction => { | ||
return typeof obj === 'function'; | ||
}; | ||
|
||
/** | ||
* Function used to cleanup Powertools for AWS resources when a Middy | ||
* middleware [returns early](https://middy.js.org/docs/intro/early-interrupt) | ||
* and terminates the middleware chain. | ||
* | ||
* When a middleware returns early, all the middleware lifecycle functions | ||
* that come after it are not executed. This means that if a middleware | ||
* was relying on certain logic to be run during the `after` or `onError` | ||
* lifecycle functions, that logic will not be executed. | ||
* | ||
* This is the case for the middlewares that are part of Powertools for AWS | ||
* which rely on these lifecycle functions to perform cleanup operations | ||
* like closing the current segment in the tracer or flushing any stored | ||
* metrics. | ||
* | ||
* When authoring a middleware that might return early, you can use this | ||
* function to cleanup Powertools resources. This function will check if | ||
* any cleanup function is present in the `request.internal` object and | ||
* execute it. | ||
* | ||
* @example | ||
* ```typescript | ||
* import middy from '@middy/core'; | ||
* import { cleanupMiddlewares } from '@aws-lambda-powertools/commons/lib/middleware'; | ||
* | ||
* // Example middleware that returns early | ||
* const myCustomMiddleware = (): middy.MiddlewareObj => { | ||
* const before = async (request: middy.Request): Promise<undefined | string> => { | ||
* // If the request is a GET, return early (as an example) | ||
* if (request.event.httpMethod === 'GET') { | ||
* // Cleanup Powertools resources | ||
* await cleanupMiddlewares(request); | ||
* // Then return early | ||
* return 'GET method not supported'; | ||
* } | ||
* }; | ||
* | ||
* return { | ||
* before, | ||
* }; | ||
* }; | ||
* ``` | ||
* | ||
* @param request - The Middy request object | ||
* @param options - An optional object that can be used to pass options to the function | ||
*/ | ||
const cleanupMiddlewares = async (request: MiddyLikeRequest): Promise<void> => { | ||
const cleanupFunctionNames = [ | ||
TRACER_KEY, | ||
METRICS_KEY, | ||
LOGGER_KEY, | ||
IDEMPOTENCY_KEY, | ||
]; | ||
for (const functionName of cleanupFunctionNames) { | ||
if (Object(request.internal).hasOwnProperty(functionName)) { | ||
const functionReference = request.internal[functionName]; | ||
if (isFunction(functionReference)) { | ||
await functionReference(request); | ||
} | ||
} | ||
} | ||
}; | ||
|
||
export { cleanupMiddlewares }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
/** | ||
* These constants are used to store cleanup functions in Middy's `request.internal` object. | ||
* They are used by the `cleanupPowertools` function to check if any cleanup function | ||
* is present and execute it. | ||
*/ | ||
const PREFIX = 'powertools-for-aws'; | ||
const TRACER_KEY = `${PREFIX}.tracer`; | ||
const METRICS_KEY = `${PREFIX}.metrics`; | ||
const LOGGER_KEY = `${PREFIX}.logger`; | ||
const IDEMPOTENCY_KEY = `${PREFIX}.idempotency`; | ||
|
||
export { TRACER_KEY, METRICS_KEY, LOGGER_KEY, IDEMPOTENCY_KEY }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from './cleanupMiddlewares'; | ||
export * from './constants'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
/** | ||
* Test Middy cleanupMiddlewares function | ||
* | ||
* @group unit/commons/cleanupMiddlewares | ||
*/ | ||
import { | ||
cleanupMiddlewares, | ||
TRACER_KEY, | ||
METRICS_KEY, | ||
} from '../../src/middleware'; | ||
import { helloworldContext as context } from '../../src/samples/resources/contexts/hello-world'; | ||
|
||
describe('Function: cleanupMiddlewares', () => { | ||
it('calls the cleanup function that are present', async () => { | ||
// Prepare | ||
const mockCleanupFunction1 = jest.fn(); | ||
const mockCleanupFunction2 = jest.fn(); | ||
const mockRequest = { | ||
event: {}, | ||
context: context, | ||
response: null, | ||
error: null, | ||
internal: { | ||
[TRACER_KEY]: mockCleanupFunction1, | ||
[METRICS_KEY]: mockCleanupFunction2, | ||
}, | ||
}; | ||
|
||
// Act | ||
await cleanupMiddlewares(mockRequest); | ||
|
||
// Assess | ||
expect(mockCleanupFunction1).toHaveBeenCalledTimes(1); | ||
expect(mockCleanupFunction1).toHaveBeenCalledWith(mockRequest); | ||
expect(mockCleanupFunction2).toHaveBeenCalledTimes(1); | ||
expect(mockCleanupFunction2).toHaveBeenCalledWith(mockRequest); | ||
}); | ||
it('resolves successfully if no cleanup function is present', async () => { | ||
// Prepare | ||
const mockRequest = { | ||
event: {}, | ||
context: context, | ||
response: null, | ||
error: null, | ||
internal: {}, | ||
}; | ||
|
||
// Act & Assess | ||
await expect(cleanupMiddlewares(mockRequest)).resolves.toBeUndefined(); | ||
}); | ||
it('resolves successfully if cleanup function is not a function', async () => { | ||
// Prepare | ||
const mockRequest = { | ||
event: {}, | ||
context: context, | ||
response: null, | ||
error: null, | ||
internal: { | ||
[TRACER_KEY]: 'not a function', | ||
}, | ||
}; | ||
|
||
// Act & Assess | ||
await expect(cleanupMiddlewares(mockRequest)).resolves.toBeUndefined(); | ||
}); | ||
}); |