1
1
import { IdempotencyHandler } from '../IdempotencyHandler' ;
2
2
import { IdempotencyConfig } from '../IdempotencyConfig' ;
3
- import { cleanupMiddlewares } from '@aws-lambda-powertools/commons/lib/middleware' ;
3
+ import {
4
+ cleanupMiddlewares ,
5
+ IDEMPOTENCY_KEY ,
6
+ } from '@aws-lambda-powertools/commons/lib/middleware' ;
4
7
import {
5
8
IdempotencyInconsistentStateError ,
6
9
IdempotencyItemAlreadyExistsError ,
@@ -9,51 +12,94 @@ import {
9
12
import { IdempotencyRecord } from '../persistence' ;
10
13
import { MAX_RETRIES } from '../constants' ;
11
14
import type { IdempotencyLambdaHandlerOptions } from '../types' ;
15
+ import type { BasePersistenceLayerInterface } from '../persistence' ;
12
16
import {
13
17
MiddlewareLikeObj ,
14
18
MiddyLikeRequest ,
15
19
JSONValue ,
16
20
} from '@aws-lambda-powertools/commons' ;
17
21
22
+ /**
23
+ * @internal
24
+ * Utility function to get the persistence store from the request internal storage
25
+ *
26
+ * @param request The Middy request object
27
+ * @returns The persistence store from the request internal
28
+ */
29
+ const getPersistenceStoreFromRequestInternal = (
30
+ request : MiddyLikeRequest
31
+ ) : BasePersistenceLayerInterface => {
32
+ const persistenceStore = request . internal [
33
+ `${ IDEMPOTENCY_KEY } .idempotencyPersistenceStore`
34
+ ] as BasePersistenceLayerInterface ;
35
+
36
+ return persistenceStore ;
37
+ } ;
38
+
39
+ /**
40
+ * @internal
41
+ * Utility function to set the persistence store in the request internal storage
42
+ *
43
+ * @param request The Middy request object
44
+ * @param persistenceStore The persistence store to set in the request internal
45
+ */
46
+ const setPersistenceStoreInRequestInternal = (
47
+ request : MiddyLikeRequest ,
48
+ persistenceStore : BasePersistenceLayerInterface
49
+ ) : void => {
50
+ request . internal [ `${ IDEMPOTENCY_KEY } .idempotencyPersistenceStore` ] =
51
+ persistenceStore ;
52
+ } ;
53
+
54
+ /**
55
+ * @internal
56
+ * Utility function to set a flag in the request internal storage to skip the idempotency middleware
57
+ * This is used to skip the idempotency middleware when the idempotency key is not present in the payload
58
+ * or when idempotency is disabled
59
+ *
60
+ * @param request The Middy request object
61
+ */
62
+ const setIdempotencySkipFlag = ( request : MiddyLikeRequest ) : void => {
63
+ request . internal [ `${ IDEMPOTENCY_KEY } .skip` ] = true ;
64
+ } ;
65
+
66
+ /**
67
+ * @internal
68
+ * Utility function to get the idempotency key from the request internal storage
69
+ * and determine if the request should skip the idempotency middleware
70
+ *
71
+ * @param request The Middy request object
72
+ * @returns Whether the idempotency middleware should be skipped
73
+ */
74
+ const shouldSkipIdempotency = ( request : MiddyLikeRequest ) : boolean => {
75
+ return request . internal [ `${ IDEMPOTENCY_KEY } .skip` ] === true ;
76
+ } ;
77
+
18
78
/**
19
79
* A middy middleware to make your Lambda Handler idempotent.
20
80
*
21
81
* @example
22
82
* ```typescript
23
- * import {
24
- * makeHandlerIdempotent,
25
- * DynamoDBPersistenceLayer,
26
- * } from '@aws-lambda-powertools/idempotency';
83
+ * import { makeHandlerIdempotent } from '@aws-lambda-powertools/idempotency/middleware';
84
+ * import { DynamoDBPersistenceLayer } from '@aws-lambda-powertools/idempotency/dynamodb';
27
85
* import middy from '@middy/core';
28
86
*
29
- * const dynamoDBPersistenceLayer = new DynamoDBPersistenceLayer({
30
- * tableName: 'idempotencyTable',
87
+ * const persistenceStore = new DynamoDBPersistenceLayer({
88
+ * tableName: 'idempotencyTable',
31
89
* });
32
90
*
33
- * const lambdaHandler = async (_event: unknown, _context: unknown) => {
34
- * //...
35
- * };
36
- *
37
- * export const handler = middy(lambdaHandler)
38
- * .use(makeHandlerIdempotent({ persistenceStore: dynamoDBPersistenceLayer }));
91
+ * export const handler = middy(
92
+ * async (_event: unknown, _context: unknown): Promise<void> => {
93
+ * // your code goes here
94
+ * }
95
+ * ).use(makeHandlerIdempotent({ persistenceStore: dynamoDBPersistenceLayer }));
39
96
* ```
40
97
*
41
98
* @param options - Options for the idempotency middleware
42
99
*/
43
100
const makeHandlerIdempotent = (
44
101
options : IdempotencyLambdaHandlerOptions
45
102
) : MiddlewareLikeObj => {
46
- const idempotencyConfig = options . config
47
- ? options . config
48
- : new IdempotencyConfig ( { } ) ;
49
- const persistenceStore = options . persistenceStore ;
50
- persistenceStore . configure ( {
51
- config : idempotencyConfig ,
52
- } ) ;
53
-
54
- // keep the flag for after and onError checks
55
- let shouldSkipIdempotency = false ;
56
-
57
103
/**
58
104
* Function called before the handler is executed.
59
105
*
@@ -76,18 +122,34 @@ const makeHandlerIdempotent = (
76
122
request : MiddyLikeRequest ,
77
123
retryNo = 0
78
124
) : Promise < unknown | void > => {
125
+ const idempotencyConfig = options . config
126
+ ? options . config
127
+ : new IdempotencyConfig ( { } ) ;
128
+ const persistenceStore = options . persistenceStore ;
129
+ persistenceStore . configure ( {
130
+ config : idempotencyConfig ,
131
+ } ) ;
132
+
79
133
if (
134
+ ! idempotencyConfig . isEnabled ( ) ||
80
135
IdempotencyHandler . shouldSkipIdempotency (
81
136
idempotencyConfig . eventKeyJmesPath ,
82
137
idempotencyConfig . throwOnNoIdempotencyKey ,
83
138
request . event as JSONValue
84
139
)
85
140
) {
86
141
// set the flag to skip checks in after and onError
87
- shouldSkipIdempotency = true ;
142
+ setIdempotencySkipFlag ( request ) ;
88
143
89
144
return ;
90
145
}
146
+
147
+ /**
148
+ * Store the persistence store in the request internal so that it can be
149
+ * used in after and onError
150
+ */
151
+ setPersistenceStoreInRequestInternal ( request , persistenceStore ) ;
152
+
91
153
try {
92
154
await persistenceStore . saveInProgress (
93
155
request . event as JSONValue ,
@@ -129,6 +191,7 @@ const makeHandlerIdempotent = (
129
191
}
130
192
}
131
193
} ;
194
+
132
195
/**
133
196
* Function called after the handler has executed successfully.
134
197
*
@@ -139,9 +202,10 @@ const makeHandlerIdempotent = (
139
202
* @param request - The Middy request object
140
203
*/
141
204
const after = async ( request : MiddyLikeRequest ) : Promise < void > => {
142
- if ( shouldSkipIdempotency ) {
205
+ if ( shouldSkipIdempotency ( request ) ) {
143
206
return ;
144
207
}
208
+ const persistenceStore = getPersistenceStoreFromRequestInternal ( request ) ;
145
209
try {
146
210
await persistenceStore . saveSuccess (
147
211
request . event as JSONValue ,
@@ -164,9 +228,10 @@ const makeHandlerIdempotent = (
164
228
* @param request - The Middy request object
165
229
*/
166
230
const onError = async ( request : MiddyLikeRequest ) : Promise < void > => {
167
- if ( shouldSkipIdempotency ) {
231
+ if ( shouldSkipIdempotency ( request ) ) {
168
232
return ;
169
233
}
234
+ const persistenceStore = getPersistenceStoreFromRequestInternal ( request ) ;
170
235
try {
171
236
await persistenceStore . deleteRecord ( request . event as JSONValue ) ;
172
237
} catch ( error ) {
@@ -177,19 +242,11 @@ const makeHandlerIdempotent = (
177
242
}
178
243
} ;
179
244
180
- if ( idempotencyConfig . isEnabled ( ) ) {
181
- return {
182
- before,
183
- after,
184
- onError,
185
- } ;
186
- } else {
187
- return {
188
- before : ( ) => {
189
- return undefined ;
190
- } ,
191
- } ;
192
- }
245
+ return {
246
+ before,
247
+ after,
248
+ onError,
249
+ } ;
193
250
} ;
194
251
195
252
export { makeHandlerIdempotent } ;
0 commit comments