11/*
22 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3- *
3+ *
44 * Licensed under the Apache License, Version 2.0 (the "License").
55 * You may not use this file except in compliance with the License.
66 * A copy of the License is located at
7- *
7+ *
88 * http://aws.amazon.com/apache2.0
9- *
9+ *
1010 * or in the "license" file accompanying this file. This file is distributed
1111 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
1212 * express or implied. See the License for the specific language governing
@@ -37,16 +37,17 @@ public abstract class BasePersistenceStore : IPersistenceStore
3737 /// Idempotency Options
3838 /// </summary>
3939 private IdempotencyOptions _idempotencyOptions = null ! ;
40-
40+
4141 /// <summary>
4242 /// Function name
4343 /// </summary>
4444 private string _functionName ;
45+
4546 /// <summary>
4647 /// Boolean to indicate whether or not payload validation is enabled
4748 /// </summary>
4849 protected bool PayloadValidationEnabled ;
49-
50+
5051 /// <summary>
5152 /// LRUCache
5253 /// </summary>
@@ -57,34 +58,45 @@ public abstract class BasePersistenceStore : IPersistenceStore
5758 /// </summary>
5859 /// <param name="idempotencyOptions">Idempotency configuration settings</param>
5960 /// <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 )
6163 {
62- var funcEnv = Environment . GetEnvironmentVariable ( Constants . LambdaFunctionNameEnv ) ;
63- _functionName = funcEnv ?? "testFunction" ;
64- if ( ! string . IsNullOrWhiteSpace ( functionName ) )
64+ if ( ! string . IsNullOrEmpty ( keyPrefix ) )
6565 {
66- _functionName += "." + functionName ;
66+ _functionName = keyPrefix ;
6767 }
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+
6879 _idempotencyOptions = idempotencyOptions ;
69-
80+
7081 if ( ! string . IsNullOrWhiteSpace ( _idempotencyOptions . PayloadValidationJmesPath ) )
7182 {
7283 PayloadValidationEnabled = true ;
7384 }
74-
85+
7586 var useLocalCache = _idempotencyOptions . UseLocalCache ;
7687 if ( useLocalCache )
7788 {
7889 _cache = new LRUCache < string , DataRecord > ( _idempotencyOptions . LocalCacheMaxItems ) ;
7990 }
8091 }
81-
92+
8293 /// <summary>
8394 /// For test purpose only (adding a cache to mock)
8495 /// </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 )
8698 {
87- Configure ( options , functionName ) ;
99+ Configure ( options , functionName , keyPrefix ) ;
88100 _cache = cache ;
89101 }
90102
@@ -118,12 +130,12 @@ public virtual async Task SaveSuccess(JsonDocument data, object result, DateTime
118130 public virtual async Task SaveInProgress ( JsonDocument data , DateTimeOffset now , double ? remainingTimeInMs )
119131 {
120132 var idempotencyKey = GetHashedIdempotencyKey ( data ) ;
121-
133+
122134 if ( RetrieveFromCache ( idempotencyKey , now ) != null )
123135 {
124136 throw new IdempotencyItemAlreadyExistsException ( ) ;
125137 }
126-
138+
127139 long ? inProgressExpirationMsTimestamp = null ;
128140 if ( remainingTimeInMs . HasValue )
129141 {
@@ -137,11 +149,10 @@ public virtual async Task SaveInProgress(JsonDocument data, DateTimeOffset now,
137149 null ,
138150 GetHashedPayload ( data ) ,
139151 inProgressExpirationMsTimestamp
140-
141152 ) ;
142153 await PutRecord ( record , now ) ;
143154 }
144-
155+
145156 /// <summary>
146157 /// Delete record from the persistence store
147158 /// </summary>
@@ -152,14 +163,14 @@ public virtual async Task DeleteRecord(JsonDocument data, Exception throwable)
152163 var idemPotencyKey = GetHashedIdempotencyKey ( data ) ;
153164
154165 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}" ,
156167 throwable . GetType ( ) . Name ,
157168 idemPotencyKey ) ;
158169
159170 await DeleteRecord ( idemPotencyKey ) ;
160171 DeleteFromCache ( idemPotencyKey ) ;
161172 }
162-
173+
163174 /// <summary>
164175 /// Retrieve idempotency key for data provided, fetch from persistence store, and convert to DataRecord.
165176 /// </summary>
@@ -182,7 +193,7 @@ public virtual async Task<DataRecord> GetRecord(JsonDocument data, DateTimeOffse
182193 ValidatePayload ( data , record ) ;
183194 return record ;
184195 }
185-
196+
186197 /// <summary>
187198 /// Save data_record to local cache except when status is "INPROGRESS"
188199 /// 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)
198209
199210 _cache . Set ( dataRecord . IdempotencyKey , dataRecord ) ;
200211 }
201-
212+
202213 /// <summary>
203214 /// Validate that the hashed payload matches data provided and stored data record
204215 /// </summary>
@@ -215,7 +226,7 @@ private void ValidatePayload(JsonDocument data, DataRecord dataRecord)
215226 throw new IdempotencyValidationException ( "Payload does not match stored record for this event key" ) ;
216227 }
217228 }
218-
229+
219230 /// <summary>
220231 /// Retrieve data record from cache
221232 /// </summary>
@@ -228,14 +239,15 @@ private DataRecord RetrieveFromCache(string idempotencyKey, DateTimeOffset now)
228239 return null ;
229240
230241 if ( ! _cache . TryGet ( idempotencyKey , out var record ) || record == null ) return null ;
231- if ( ! record . IsExpired ( now ) )
242+ if ( ! record . IsExpired ( now ) )
232243 {
233244 return record ;
234245 }
246+
235247 DeleteFromCache ( idempotencyKey ) ;
236248 return null ;
237249 }
238-
250+
239251 /// <summary>
240252 /// Deletes item from cache
241253 /// </summary>
@@ -244,10 +256,10 @@ private void DeleteFromCache(string idempotencyKey)
244256 {
245257 if ( ! _idempotencyOptions . UseLocalCache )
246258 return ;
247-
259+
248260 _cache . Delete ( idempotencyKey ) ;
249261 }
250-
262+
251263 /// <summary>
252264 /// Extract payload using validation key jmespath and return a hashed representation
253265 /// </summary>
@@ -259,12 +271,12 @@ private string GetHashedPayload(JsonDocument data)
259271 {
260272 return "" ;
261273 }
262-
274+
263275 var transformer = JsonTransformer . Parse ( _idempotencyOptions . PayloadValidationJmesPath ) ;
264276 var result = transformer . Transform ( data . RootElement ) ;
265277 return GenerateHash ( result . RootElement ) ;
266278 }
267-
279+
268280 /// <summary>
269281 /// Calculate unix timestamp of expiry date for idempotency record
270282 /// </summary>
@@ -285,7 +297,7 @@ private string GetHashedIdempotencyKey(JsonDocument data)
285297 {
286298 var node = data . RootElement ;
287299 var eventKeyJmesPath = _idempotencyOptions . EventKeyJmesPath ;
288- if ( eventKeyJmesPath != null )
300+ if ( eventKeyJmesPath != null )
289301 {
290302 var transformer = JsonTransformer . Parse ( eventKeyJmesPath ) ;
291303 var result = transformer . Transform ( node ) ;
@@ -298,7 +310,9 @@ private string GetHashedIdempotencyKey(JsonDocument data)
298310 {
299311 throw new IdempotencyKeyException ( "No data found to create a hashed idempotency key" ) ;
300312 }
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 ) ;
302316 }
303317
304318 var hash = GenerateHash ( node ) ;
@@ -313,9 +327,10 @@ private string GetHashedIdempotencyKey(JsonDocument data)
313327 private static bool IsMissingIdempotencyKey ( JsonElement data )
314328 {
315329 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 ) ;
317332 }
318-
333+
319334 /// <summary>
320335 /// Generate a hash value from the provided data
321336 /// </summary>
@@ -328,16 +343,16 @@ internal string GenerateHash(JsonElement data)
328343 // starting .NET 8 no option to change hash algorithm
329344 using var hashAlgorithm = MD5 . Create ( ) ;
330345#else
331-
332346 using var hashAlgorithm = HashAlgorithm . Create ( _idempotencyOptions . HashFunction ) ;
333347#endif
334348 if ( hashAlgorithm == null )
335349 {
336350 throw new ArgumentException ( "Invalid HashAlgorithm" ) ;
337351 }
352+
338353 var stringToHash = data . ToString ( ) ;
339354 var hash = GetHash ( hashAlgorithm , stringToHash ) ;
340-
355+
341356 return hash ;
342357 }
343358
@@ -351,18 +366,18 @@ private static string GetHash(HashAlgorithm hashAlgorithm, string input)
351366 {
352367 // Convert the input string to a byte array and compute the hash.
353368 var data = hashAlgorithm . ComputeHash ( Encoding . UTF8 . GetBytes ( input ) ) ;
354-
369+
355370 // Create a new Stringbuilder to collect the bytes
356371 // and create a string.
357372 var sBuilder = new StringBuilder ( ) ;
358-
373+
359374 // Loop through each byte of the hashed data
360375 // and format each one as a hexadecimal string.
361376 for ( var i = 0 ; i < data . Length ; i ++ )
362377 {
363378 sBuilder . Append ( data [ i ] . ToString ( "x2" ) ) ;
364379 }
365-
380+
366381 // Return the hexadecimal string.
367382 return sBuilder . ToString ( ) ;
368383 }
0 commit comments