fix(idempotency): fix str(None) in Redis persistence and extract duplicated null-check helper - tech debt 8090#8176
Conversation
…istence Replace str(item.get(...)) with item.get(...) to avoid storing the string "None" when a value is missing from Redis hash map. When data_attr or validation_key_attr is missing from Redis, item.get() returns None. Wrapping with str() converts it to the string "None" which is incorrect. Now correctly returns None. Part of aws-powertools#8090 Signed-off-by: hirenkumar-n-dholariya <hirenkumarnd@gmail.com>
…into helper method Replace 4 identical null-check blocks across save_success, save_inprogress, delete_record, and get_record with a single helper method _get_idempotency_key_or_return_none() to reduce code duplication. The helper encapsulates the pattern of calling _get_hashed_idempotency_key() and returning None early if the key is None, keeping each method cleaner and easier to read. Part of aws-powertools#8090 Signed-off-by: hirenkumar-n-dholariya <hirenkumarnd@gmail.com>
…wertools#8090 Fix 1 - str(None) in Redis _item_to_data_record: - Missing data_attr returns None not string "None" - Existing data_attr returns value correctly - Demonstrates old bug vs new correct behavior Fix 2 - _get_idempotency_key_or_return_none helper: - Returns None when key is None - Returns key string when key exists - Correctly used in save_success, save_inprogress, delete_record, and get_record (all return None early) Part of aws-powertools#8090 Signed-off-by: hirenkumar-n-dholariya <hirenkumarnd@gmail.com>
|
No acknowledgement section found. Please make sure you used the template to open a PR and didn't remove the acknowledgment section. Check the template at |
|
leandrodamascena
left a comment
There was a problem hiding this comment.
Hey @hirenkumar-n-dholariya, thanks for working on this and submitting the PR!
The Redis str(None) fix is good. Removing the str() wrapper is the right fix.
However, I have some concerns with the rest of the PR, and there are missing items from the issue.
Missing items from the issue
In your comment on #8090 you mentioned that items 1 and 2 "appear already resolved", but they are still present in the codebase:
Item 1 - Falsy response handling is still in base.py:299:
serialized_response: dict = self.output_serializer.to_dict(response) if response else NoneThis silently discards valid falsy return values like 0, False, "", [], {}. It should be if response is not None. This is a real bug that affects customers.
Item 2 - "INPROGRESS" string literal is still in base.py:170:
if is_replay and record is not None and record.status == "INPROGRESS":Should use STATUS_CONSTANTS["INPROGRESS"] for consistency with the rest of the code.
Helper method
The helper _get_idempotency_key_or_return_none() doesn't actually reduce the duplication. After the refactor, each caller still does:
idempotency_key = self._get_idempotency_key_or_return_none(data=data)
if idempotency_key is None:
return NoneWe moved one line into a wrapper but the if None: return is still in all 4 methods. The helper is a passthrough that adds indirection without simplifying anything. Let's drop this part and keep the original inline pattern, it's 3 lines and every Python developer reads it instantly.
Tests
The tests need a full rework. Please don't create a new test file for this.
We already have test_item_to_datarecord_conversion in tests/functional/idempotency/_redis/test_redis_layer.py (line 318) that tests exactly _item_to_data_record(). Add your test case there, next to the existing one. That's our pattern: tests go in the existing test files, close to related tests.
What's wrong with the current tests:
- Don't test Python builtins.
test_str_none_produces_wrong_stringasserts thatstr(None) == "None"andtest_redis_missing_data_attr_returns_none_not_stringasserts thatdict.get()returnsNone. These are testing the Python language, not our code. - Don't mock internal methods with MagicMock. We don't do that in idempotency tests. Look at the existing tests in
_redis/test_redis_layer.pyand_boto3/test_idempotency.pyfor the patterns we use. We test through the actual classes with fixtures, not by patching_get_hashed_idempotency_keywith a MagicMock. ConcretePersistenceLayeris defined 5 times instead of once.- 208 lines of tests for a 2-line fix is too much surface area. A single test in the existing file that calls
_item_to_data_record()with missing attributes and assertsresponse_data is None(not"None") would cover the Redis fix properly.
Suggestion
Let's scope this PR to fix all 4 items from the issue:
- Fix
if response else Nonetoif response is not None(item 1 - bug fix) - Fix
"INPROGRESS"string literal to useSTATUS_CONSTANTS(item 2 - consistency) - Fix
str(None)in Redis (item 3 - already done, good) - Drop the helper method extraction (item 4 - the current approach doesn't improve things)
Add tests in the existing test files following our current patterns. Also pls fix the issues in SonarCloud.
Thanks again for contributing!




Issue number: fixes #8090
Summary
Fixes two tech debt issues found during code review of the idempotency utility.
Changes
persistence/redis.pystr(None)producing the string"None"in_item_to_data_record()when
data_attrorvalidation_key_attris missing from Redis.Changed
str(item.get(...))toitem.get(...)so missing values correctlyreturn
Noneinstead of the string"None".persistence/base.pysave_success,save_inprogress,delete_record, andget_record) into asingle reusable helper method
_get_idempotency_key_or_return_none().User experience
Before:
"None"string stored instead of
NoneAfter:
NoneNotes on remaining items from the issue
After reviewing the current codebase:
if response else None): Does not appear incurrent code — appears already resolved upstream
STATUS_CONSTANTS["INPROGRESS"]throughout — no change needed
Checklist
str(None)fix and new helper methodAcknowledgment
under the terms of your choice.
By submitting this pull request, I confirm that you can use, modify, copy,
and redistribute this contribution, under the terms of your choice.