1
1
/*
2
2
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
- *
3
+ *
4
4
* Licensed under the Apache License, Version 2.0 (the "License").
5
5
* You may not use this file except in compliance with the License.
6
6
* A copy of the License is located at
7
- *
7
+ *
8
8
* http://aws.amazon.com/apache2.0
9
- *
9
+ *
10
10
* or in the "license" file accompanying this file. This file is distributed
11
11
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12
12
* express or implied. See the License for the specific language governing
@@ -37,16 +37,17 @@ public abstract class BasePersistenceStore : IPersistenceStore
37
37
/// Idempotency Options
38
38
/// </summary>
39
39
private IdempotencyOptions _idempotencyOptions = null ! ;
40
-
40
+
41
41
/// <summary>
42
42
/// Function name
43
43
/// </summary>
44
44
private string _functionName ;
45
+
45
46
/// <summary>
46
47
/// Boolean to indicate whether or not payload validation is enabled
47
48
/// </summary>
48
49
protected bool PayloadValidationEnabled ;
49
-
50
+
50
51
/// <summary>
51
52
/// LRUCache
52
53
/// </summary>
@@ -57,34 +58,45 @@ public abstract class BasePersistenceStore : IPersistenceStore
57
58
/// </summary>
58
59
/// <param name="idempotencyOptions">Idempotency configuration settings</param>
59
60
/// <param name="functionName">The name of the function being decorated</param>
60
- public void Configure ( IdempotencyOptions idempotencyOptions , string functionName )
61
+ /// <param name="keyPrefix"></param>
62
+ public void Configure ( IdempotencyOptions idempotencyOptions , string functionName , string keyPrefix )
61
63
{
62
- var funcEnv = Environment . GetEnvironmentVariable ( Constants . LambdaFunctionNameEnv ) ;
63
- _functionName = funcEnv ?? "testFunction" ;
64
- if ( ! string . IsNullOrWhiteSpace ( functionName ) )
64
+ if ( ! string . IsNullOrEmpty ( keyPrefix ) )
65
65
{
66
- _functionName += "." + functionName ;
66
+ _functionName = keyPrefix ;
67
67
}
68
+ else
69
+ {
70
+ var funcEnv = Environment . GetEnvironmentVariable ( Constants . LambdaFunctionNameEnv ) ;
71
+
72
+ _functionName = funcEnv ?? "testFunction" ;
73
+ if ( ! string . IsNullOrWhiteSpace ( functionName ) )
74
+ {
75
+ _functionName += "." + functionName ;
76
+ }
77
+ }
78
+
68
79
_idempotencyOptions = idempotencyOptions ;
69
-
80
+
70
81
if ( ! string . IsNullOrWhiteSpace ( _idempotencyOptions . PayloadValidationJmesPath ) )
71
82
{
72
83
PayloadValidationEnabled = true ;
73
84
}
74
-
85
+
75
86
var useLocalCache = _idempotencyOptions . UseLocalCache ;
76
87
if ( useLocalCache )
77
88
{
78
89
_cache = new LRUCache < string , DataRecord > ( _idempotencyOptions . LocalCacheMaxItems ) ;
79
90
}
80
91
}
81
-
92
+
82
93
/// <summary>
83
94
/// For test purpose only (adding a cache to mock)
84
95
/// </summary>
85
- internal void Configure ( IdempotencyOptions options , string functionName , LRUCache < string , DataRecord > cache )
96
+ internal void Configure ( IdempotencyOptions options , string functionName , string keyPrefix ,
97
+ LRUCache < string , DataRecord > cache )
86
98
{
87
- Configure ( options , functionName ) ;
99
+ Configure ( options , functionName , keyPrefix ) ;
88
100
_cache = cache ;
89
101
}
90
102
@@ -118,12 +130,12 @@ public virtual async Task SaveSuccess(JsonDocument data, object result, DateTime
118
130
public virtual async Task SaveInProgress ( JsonDocument data , DateTimeOffset now , double ? remainingTimeInMs )
119
131
{
120
132
var idempotencyKey = GetHashedIdempotencyKey ( data ) ;
121
-
133
+
122
134
if ( RetrieveFromCache ( idempotencyKey , now ) != null )
123
135
{
124
136
throw new IdempotencyItemAlreadyExistsException ( ) ;
125
137
}
126
-
138
+
127
139
long ? inProgressExpirationMsTimestamp = null ;
128
140
if ( remainingTimeInMs . HasValue )
129
141
{
@@ -137,11 +149,10 @@ public virtual async Task SaveInProgress(JsonDocument data, DateTimeOffset now,
137
149
null ,
138
150
GetHashedPayload ( data ) ,
139
151
inProgressExpirationMsTimestamp
140
-
141
152
) ;
142
153
await PutRecord ( record , now ) ;
143
154
}
144
-
155
+
145
156
/// <summary>
146
157
/// Delete record from the persistence store
147
158
/// </summary>
@@ -152,14 +163,14 @@ public virtual async Task DeleteRecord(JsonDocument data, Exception throwable)
152
163
var idemPotencyKey = GetHashedIdempotencyKey ( data ) ;
153
164
154
165
Console . WriteLine ( "Function raised an exception {0}. " +
155
- "Clearing in progress record in persistence store for idempotency key: {1}" ,
166
+ "Clearing in progress record in persistence store for idempotency key: {1}" ,
156
167
throwable . GetType ( ) . Name ,
157
168
idemPotencyKey ) ;
158
169
159
170
await DeleteRecord ( idemPotencyKey ) ;
160
171
DeleteFromCache ( idemPotencyKey ) ;
161
172
}
162
-
173
+
163
174
/// <summary>
164
175
/// Retrieve idempotency key for data provided, fetch from persistence store, and convert to DataRecord.
165
176
/// </summary>
@@ -182,7 +193,7 @@ public virtual async Task<DataRecord> GetRecord(JsonDocument data, DateTimeOffse
182
193
ValidatePayload ( data , record ) ;
183
194
return record ;
184
195
}
185
-
196
+
186
197
/// <summary>
187
198
/// Save data_record to local cache except when status is "INPROGRESS"
188
199
/// NOTE: We can't cache "INPROGRESS" records as we have no way to reflect updates that can happen outside of the
@@ -198,7 +209,7 @@ private void SaveToCache(DataRecord dataRecord)
198
209
199
210
_cache . Set ( dataRecord . IdempotencyKey , dataRecord ) ;
200
211
}
201
-
212
+
202
213
/// <summary>
203
214
/// Validate that the hashed payload matches data provided and stored data record
204
215
/// </summary>
@@ -215,7 +226,7 @@ private void ValidatePayload(JsonDocument data, DataRecord dataRecord)
215
226
throw new IdempotencyValidationException ( "Payload does not match stored record for this event key" ) ;
216
227
}
217
228
}
218
-
229
+
219
230
/// <summary>
220
231
/// Retrieve data record from cache
221
232
/// </summary>
@@ -228,14 +239,15 @@ private DataRecord RetrieveFromCache(string idempotencyKey, DateTimeOffset now)
228
239
return null ;
229
240
230
241
if ( ! _cache . TryGet ( idempotencyKey , out var record ) || record == null ) return null ;
231
- if ( ! record . IsExpired ( now ) )
242
+ if ( ! record . IsExpired ( now ) )
232
243
{
233
244
return record ;
234
245
}
246
+
235
247
DeleteFromCache ( idempotencyKey ) ;
236
248
return null ;
237
249
}
238
-
250
+
239
251
/// <summary>
240
252
/// Deletes item from cache
241
253
/// </summary>
@@ -244,10 +256,10 @@ private void DeleteFromCache(string idempotencyKey)
244
256
{
245
257
if ( ! _idempotencyOptions . UseLocalCache )
246
258
return ;
247
-
259
+
248
260
_cache . Delete ( idempotencyKey ) ;
249
261
}
250
-
262
+
251
263
/// <summary>
252
264
/// Extract payload using validation key jmespath and return a hashed representation
253
265
/// </summary>
@@ -259,12 +271,12 @@ private string GetHashedPayload(JsonDocument data)
259
271
{
260
272
return "" ;
261
273
}
262
-
274
+
263
275
var transformer = JsonTransformer . Parse ( _idempotencyOptions . PayloadValidationJmesPath ) ;
264
276
var result = transformer . Transform ( data . RootElement ) ;
265
277
return GenerateHash ( result . RootElement ) ;
266
278
}
267
-
279
+
268
280
/// <summary>
269
281
/// Calculate unix timestamp of expiry date for idempotency record
270
282
/// </summary>
@@ -285,7 +297,7 @@ private string GetHashedIdempotencyKey(JsonDocument data)
285
297
{
286
298
var node = data . RootElement ;
287
299
var eventKeyJmesPath = _idempotencyOptions . EventKeyJmesPath ;
288
- if ( eventKeyJmesPath != null )
300
+ if ( eventKeyJmesPath != null )
289
301
{
290
302
var transformer = JsonTransformer . Parse ( eventKeyJmesPath ) ;
291
303
var result = transformer . Transform ( node ) ;
@@ -298,7 +310,9 @@ private string GetHashedIdempotencyKey(JsonDocument data)
298
310
{
299
311
throw new IdempotencyKeyException ( "No data found to create a hashed idempotency key" ) ;
300
312
}
301
- Console . WriteLine ( "No data found to create a hashed idempotency key. JMESPath: {0}" , _idempotencyOptions . EventKeyJmesPath ?? string . Empty ) ;
313
+
314
+ Console . WriteLine ( "No data found to create a hashed idempotency key. JMESPath: {0}" ,
315
+ _idempotencyOptions . EventKeyJmesPath ?? string . Empty ) ;
302
316
}
303
317
304
318
var hash = GenerateHash ( node ) ;
@@ -313,9 +327,10 @@ private string GetHashedIdempotencyKey(JsonDocument data)
313
327
private static bool IsMissingIdempotencyKey ( JsonElement data )
314
328
{
315
329
return data . ValueKind == JsonValueKind . Null || data . ValueKind == JsonValueKind . Undefined
316
- || ( data . ValueKind == JsonValueKind . String && data . ToString ( ) == string . Empty ) ;
330
+ || ( data . ValueKind == JsonValueKind . String &&
331
+ data . ToString ( ) == string . Empty ) ;
317
332
}
318
-
333
+
319
334
/// <summary>
320
335
/// Generate a hash value from the provided data
321
336
/// </summary>
@@ -328,16 +343,16 @@ internal string GenerateHash(JsonElement data)
328
343
// starting .NET 8 no option to change hash algorithm
329
344
using var hashAlgorithm = MD5 . Create ( ) ;
330
345
#else
331
-
332
346
using var hashAlgorithm = HashAlgorithm . Create ( _idempotencyOptions . HashFunction ) ;
333
347
#endif
334
348
if ( hashAlgorithm == null )
335
349
{
336
350
throw new ArgumentException ( "Invalid HashAlgorithm" ) ;
337
351
}
352
+
338
353
var stringToHash = data . ToString ( ) ;
339
354
var hash = GetHash ( hashAlgorithm , stringToHash ) ;
340
-
355
+
341
356
return hash ;
342
357
}
343
358
@@ -351,18 +366,18 @@ private static string GetHash(HashAlgorithm hashAlgorithm, string input)
351
366
{
352
367
// Convert the input string to a byte array and compute the hash.
353
368
var data = hashAlgorithm . ComputeHash ( Encoding . UTF8 . GetBytes ( input ) ) ;
354
-
369
+
355
370
// Create a new Stringbuilder to collect the bytes
356
371
// and create a string.
357
372
var sBuilder = new StringBuilder ( ) ;
358
-
373
+
359
374
// Loop through each byte of the hashed data
360
375
// and format each one as a hexadecimal string.
361
376
for ( var i = 0 ; i < data . Length ; i ++ )
362
377
{
363
378
sBuilder . Append ( data [ i ] . ToString ( "x2" ) ) ;
364
379
}
365
-
380
+
366
381
// Return the hexadecimal string.
367
382
return sBuilder . ToString ( ) ;
368
383
}
0 commit comments