Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(idempotency): idempotent_function should support standalone falsy values #1669

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions aws_lambda_powertools/utilities/idempotency/idempotency.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,14 +134,14 @@ def decorate(*args, **kwargs):
if os.getenv(constants.IDEMPOTENCY_DISABLED_ENV):
return function(*args, **kwargs)

payload = kwargs.get(data_keyword_argument)

if not payload:
if data_keyword_argument not in kwargs:
raise RuntimeError(
f"Unable to extract '{data_keyword_argument}' from keyword arguments."
f" Ensure this exists in your function's signature as well as the caller used it as a keyword argument"
)

payload = kwargs.get(data_keyword_argument)

idempotency_handler = IdempotencyHandler(
function=function,
function_payload=payload,
Expand Down
42 changes: 41 additions & 1 deletion tests/functional/idempotency/test_idempotency.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def test_idempotent_lambda_already_completed(
lambda_context,
):
"""
Test idempotent decorator where event with matching event key has already been succesfully processed
Test idempotent decorator where event with matching event key has already been successfully processed
"""

stubber = stub.Stubber(persistence_store.table.meta.client)
Expand Down Expand Up @@ -1247,6 +1247,46 @@ def lambda_handler(event, _):
assert handler_result == expected_result


@pytest.mark.parametrize("data", [None, 0, False])
def test_idempotent_function_falsy_values(data):
# Scenario to validate we can use idempotent_function with any function
# receiving a falsy value (`None`, `False`, `0`, etc.)
# shouldn't cause a RuntimeError
mock_event = data
idempotency_key = f"{TESTS_MODULE_PREFIX}.test_idempotent_function_falsy_values.<locals>.record_handler#{hash_idempotency_key(mock_event)}" # noqa: E501

persistence_layer = MockPersistenceLayer(expected_idempotency_key=idempotency_key)
expected_result = {"message": "Foo"}

@idempotent_function(persistence_store=persistence_layer, data_keyword_argument="record")
def record_handler(record):
return expected_result

# WHEN calling the function
result = record_handler(record=mock_event)
# THEN we expect the function to execute successfully
assert result == expected_result


@pytest.mark.parametrize("data", [None, 0, False])
def test_idempotent_function_falsy_values_with_raise_on_no_idempotency_key(
data, persistence_store: DynamoDBPersistenceLayer
):
# GIVEN raise_on_no_idempotency_key is True
idempotency_config = IdempotencyConfig(event_key_jmespath="idemKey", raise_on_no_idempotency_key=True)

@idempotent_function(data_keyword_argument="record", persistence_store=persistence_store, config=idempotency_config)
def record_handler(record):
return ValueError("Should not be raised")

# WHEN calling the function
with pytest.raises(IdempotencyKeyError) as e:
record_handler(record=data)

# THEN we expect an idempotency key error message
assert "No data found to create a hashed idempotency_key" == e.value.args[0]


def test_idempotent_data_sorting():
# Scenario to validate same data in different order hashes to the same idempotency key
data_one = {"data": "test message 1", "more_data": "more data 1"}
Expand Down