1- import type { AnyFunctionWithRecord , IdempotencyHandlerOptions } from './types' ;
1+ import type { JSONValue } from '@aws-lambda-powertools/commons' ;
2+ import type { AnyFunction , IdempotencyHandlerOptions } from './types' ;
23import { IdempotencyRecordStatus } from './types' ;
34import {
45 IdempotencyAlreadyInProgressError ,
@@ -13,31 +14,57 @@ import { search } from 'jmespath';
1314
1415/**
1516 * @internal
17+ *
18+ * Class that handles the idempotency lifecycle.
19+ *
20+ * This class is used under the hood by the Idempotency utility
21+ * and provides several methods that are called at different stages
22+ * to orchestrate the idempotency logic.
1623 */
17- export class IdempotencyHandler < U > {
18- private readonly fullFunctionPayload : Record < string , unknown > ;
19- private readonly functionPayloadToBeHashed : Record < string , unknown > ;
20- private readonly functionToMakeIdempotent : AnyFunctionWithRecord < U > ;
21- private readonly idempotencyConfig : IdempotencyConfig ;
22- private readonly persistenceStore : BasePersistenceLayer ;
24+ export class IdempotencyHandler < Func extends AnyFunction > {
25+ /**
26+ * The arguments passed to the function.
27+ *
28+ * For example, if the function is `foo(a, b)`, then `functionArguments` will be `[a, b]`.
29+ * We need to keep track of the arguments so that we can pass them to the function when we call it.
30+ */
31+ readonly #functionArguments: unknown [ ] ;
32+ /**
33+ * The payload to be hashed.
34+ *
35+ * This is the argument that is used for the idempotency.
36+ */
37+ readonly #functionPayloadToBeHashed: JSONValue ;
38+ /**
39+ * Reference to the function to be made idempotent.
40+ */
41+ readonly #functionToMakeIdempotent: AnyFunction ;
42+ /**
43+ * Idempotency configuration options.
44+ */
45+ readonly #idempotencyConfig: IdempotencyConfig ;
46+ /**
47+ * Persistence layer used to store the idempotency records.
48+ */
49+ readonly #persistenceStore: BasePersistenceLayer ;
2350
24- public constructor ( options : IdempotencyHandlerOptions < U > ) {
51+ public constructor ( options : IdempotencyHandlerOptions ) {
2552 const {
2653 functionToMakeIdempotent,
2754 functionPayloadToBeHashed,
2855 idempotencyConfig,
29- fullFunctionPayload ,
56+ functionArguments ,
3057 persistenceStore,
3158 } = options ;
32- this . functionToMakeIdempotent = functionToMakeIdempotent ;
33- this . functionPayloadToBeHashed = functionPayloadToBeHashed ;
34- this . idempotencyConfig = idempotencyConfig ;
35- this . fullFunctionPayload = fullFunctionPayload ;
59+ this . # functionToMakeIdempotent = functionToMakeIdempotent ;
60+ this . # functionPayloadToBeHashed = functionPayloadToBeHashed ;
61+ this . # idempotencyConfig = idempotencyConfig ;
62+ this . #functionArguments = functionArguments ;
3663
37- this . persistenceStore = persistenceStore ;
64+ this . # persistenceStore = persistenceStore ;
3865
39- this . persistenceStore . configure ( {
40- config : this . idempotencyConfig ,
66+ this . # persistenceStore. configure ( {
67+ config : this . # idempotencyConfig,
4168 } ) ;
4269 }
4370
@@ -69,14 +96,14 @@ export class IdempotencyHandler<U> {
6996 return idempotencyRecord . getResponse ( ) ;
7097 }
7198
72- public async getFunctionResult ( ) : Promise < U > {
73- let result : U ;
99+ public async getFunctionResult ( ) : Promise < ReturnType < Func > > {
100+ let result ;
74101 try {
75- result = await this . functionToMakeIdempotent ( this . fullFunctionPayload ) ;
102+ result = await this . # functionToMakeIdempotent( ... this . #functionArguments ) ;
76103 } catch ( e ) {
77104 try {
78- await this . persistenceStore . deleteRecord (
79- this . functionPayloadToBeHashed
105+ await this . # persistenceStore. deleteRecord (
106+ this . # functionPayloadToBeHashed
80107 ) ;
81108 } catch ( e ) {
82109 throw new IdempotencyPersistenceLayerError (
@@ -87,9 +114,9 @@ export class IdempotencyHandler<U> {
87114 throw e ;
88115 }
89116 try {
90- await this . persistenceStore . saveSuccess (
91- this . functionPayloadToBeHashed ,
92- result as Record < string , unknown >
117+ await this . # persistenceStore. saveSuccess (
118+ this . # functionPayloadToBeHashed,
119+ result
93120 ) ;
94121 } catch ( e ) {
95122 throw new IdempotencyPersistenceLayerError (
@@ -108,7 +135,7 @@ export class IdempotencyHandler<U> {
108135 * window, we might get an `IdempotencyInconsistentStateError`. In such
109136 * cases we can safely retry the handling a few times.
110137 */
111- public async handle ( ) : Promise < U > {
138+ public async handle ( ) : Promise < ReturnType < Func > > {
112139 let e ;
113140 for ( let retryNo = 0 ; retryNo <= MAX_RETRIES ; retryNo ++ ) {
114141 try {
@@ -129,34 +156,36 @@ export class IdempotencyHandler<U> {
129156 throw e ;
130157 }
131158
132- public async processIdempotency ( ) : Promise < U > {
159+ public async processIdempotency ( ) : Promise < ReturnType < Func > > {
133160 // early return if we should skip idempotency completely
134161 if (
135162 IdempotencyHandler . shouldSkipIdempotency (
136- this . idempotencyConfig . eventKeyJmesPath ,
137- this . idempotencyConfig . throwOnNoIdempotencyKey ,
138- this . fullFunctionPayload
163+ this . # idempotencyConfig. eventKeyJmesPath ,
164+ this . # idempotencyConfig. throwOnNoIdempotencyKey ,
165+ this . #functionPayloadToBeHashed
139166 )
140167 ) {
141- return await this . functionToMakeIdempotent ( this . fullFunctionPayload ) ;
168+ return await this . # functionToMakeIdempotent( ... this . #functionArguments ) ;
142169 }
143170
144171 try {
145- await this . persistenceStore . saveInProgress (
146- this . functionPayloadToBeHashed ,
147- this . idempotencyConfig . lambdaContext ?. getRemainingTimeInMillis ( )
172+ await this . # persistenceStore. saveInProgress (
173+ this . # functionPayloadToBeHashed,
174+ this . # idempotencyConfig. lambdaContext ?. getRemainingTimeInMillis ( )
148175 ) ;
149176 } catch ( e ) {
150177 if ( e instanceof IdempotencyItemAlreadyExistsError ) {
151178 const idempotencyRecord : IdempotencyRecord =
152- await this . persistenceStore . getRecord ( this . functionPayloadToBeHashed ) ;
179+ await this . #persistenceStore. getRecord (
180+ this . #functionPayloadToBeHashed
181+ ) ;
153182
154183 return IdempotencyHandler . determineResultFromIdempotencyRecord (
155184 idempotencyRecord
156- ) as U ;
185+ ) as ReturnType < Func > ;
157186 } else {
158187 throw new IdempotencyPersistenceLayerError (
159- 'Failed to save record in progress' ,
188+ 'Failed to save in progress record to idempotency store ' ,
160189 e as Error
161190 ) ;
162191 }
@@ -177,7 +206,7 @@ export class IdempotencyHandler<U> {
177206 public static shouldSkipIdempotency (
178207 eventKeyJmesPath : string ,
179208 throwOnNoIdempotencyKey : boolean ,
180- fullFunctionPayload : Record < string , unknown >
209+ fullFunctionPayload : JSONValue
181210 ) : boolean {
182211 return ( eventKeyJmesPath &&
183212 ! throwOnNoIdempotencyKey &&
0 commit comments