diff --git a/layercake/layercake/dynamodb.py b/layercake/layercake/dynamodb.py index 75e096a..6f8d291 100644 --- a/layercake/layercake/dynamodb.py +++ b/layercake/layercake/dynamodb.py @@ -1,6 +1,7 @@ import json import logging import os +import warnings from abc import ABC, abstractmethod from base64 import urlsafe_b64decode, urlsafe_b64encode from dataclasses import dataclass @@ -33,7 +34,7 @@ serializer = TypeSerializer() deserializer = TypeDeserializer() -def _serialize_to_basic_types(data: Any) -> str | dict | set: +def _serialize_to_basic_types(data: Any) -> str | dict | set | list: match data: case datetime(): return data.isoformat() @@ -42,7 +43,12 @@ def _serialize_to_basic_types(data: Any) -> str | dict | set: case IPv4Address(): return str(data) case tuple() | list(): - return set(_serialize_to_basic_types(v) for v in data) + serialized = [_serialize_to_basic_types(v) for v in data] + + if any(isinstance(v, dict) for v in serialized): + return serialized + + return set(serialized) case dict(): return {k: _serialize_to_basic_types(v) for k, v in data.items()} case _: @@ -85,6 +91,12 @@ else: *, delimiter: str = '#', ) -> str: + warnings.warn( + 'ComposeKey() is deprecated and will be removed in the future.', + DeprecationWarning, + stacklevel=2, + ) + if isinstance(keyparts, str): keyparts = (keyparts,) @@ -119,6 +131,12 @@ else: class PrefixKey(str): def __new__(cls, prefix: str, delimiter: str | None = '#') -> str: + warnings.warn( + 'PrefixKey() is deprecated and will be removed in the future.', + DeprecationWarning, + stacklevel=2, + ) + if not delimiter: return super().__new__(cls, prefix) @@ -145,12 +163,15 @@ if TYPE_CHECKING: The sort key value. path_spec: str, optional Optional specification for nested data extraction. + rename_key : str, optional + If provided, renames the sort key in the output. remove_prefix: str, optional Optional prefix to remove from the key when forming the result dict. """ sk: str path_spec: str | None = None + rename_key: str | None = None remove_prefix: str | None = None else: @@ -166,6 +187,8 @@ else: The sort key value. path_spec: str, optional Optional specification for nested data extraction. + rename_key : str, optional + If provided, renames the sort key in the output. remove_prefix: str, optional Optional prefix to remove from the key when forming the result dict. """ @@ -175,6 +198,7 @@ else: sk: str, *, path_spec: str | None = None, + rename_key: str | None = None, remove_prefix: str | None = None, ) -> str: return super().__new__(cls, sk) @@ -184,12 +208,14 @@ else: sk: str, *, path_spec: str | None = None, + rename_key: str | None = None, remove_prefix: str | None = None, ) -> None: # __init__ is used to store the parameters for later reference. # For immutable types like str, __init__ cannot change the instance's value. self.sk = sk self.path_spec = path_spec + self.rename_key = rename_key self.remove_prefix = remove_prefix @@ -1074,6 +1100,9 @@ class DynamoDBCollection: if pair.rename_key: return pair.rename_key + if getattr(sk, 'rename_key', None): + return sk.rename_key + if not isinstance(sk, SortKey): return pk diff --git a/layercake/pyproject.toml b/layercake/pyproject.toml index 7116ec4..804bff6 100644 --- a/layercake/pyproject.toml +++ b/layercake/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "layercake" -version = "0.9.10" +version = "0.9.12" description = "Packages shared dependencies to optimize deployment and ensure consistency across functions." readme = "README.md" authors = [ diff --git a/layercake/tests/test_dynamodb.py b/layercake/tests/test_dynamodb.py index 630c004..e911593 100644 --- a/layercake/tests/test_dynamodb.py +++ b/layercake/tests/test_dynamodb.py @@ -31,24 +31,42 @@ def test_serialize(): 'ip': {'S': '127.0.0.1'}, } - assert serialize( - {'ids': ('1', '2', '3')}, - ) == { - 'ids': { - 'L': [{'S': '1'}, {'S': '2'}, {'S': '3'}], - } - } + assert ( + 'SS' + in serialize( + {'ids': ('1', '2', '3')}, + )['ids'] + ) - assert serialize( - {'ids': ['1', '2', '3']}, - ) == { - 'ids': { - 'L': [{'S': '1'}, {'S': '2'}, {'S': '3'}], - } - } + assert ( + 'SS' + in serialize( + {'ids': ['1', '2', '3']}, + )['ids'] + ) assert serialize({'ids': {'1'}}) == {'ids': {'SS': ['1']}} + assert serialize( + { + 'users': [ + {'name': 'Sérgio'}, + {'name': 'Tiago'}, + ] + }, + ) == { + 'users': { + 'L': [ + { + 'M': {'name': {'S': 'Sérgio'}}, + }, + { + 'M': {'name': {'S': 'Tiago'}}, + }, + ] + } + } + def test_composekey(): key = ComposeKey(('122', 'abc'), prefix='schedules', delimiter=':') @@ -263,7 +281,11 @@ def test_collection_get_items( ) + SortKey('0') + SortKey('0') - + SortKey('metadata#billing_policy', path_spec='payment_method') + + SortKey( + 'metadata#billing_policy', + path_spec='payment_method', + rename_key='payment_method', + ) + SortKey('metadata#payment_policy', remove_prefix='metadata#'), ) @@ -273,7 +295,7 @@ def test_collection_get_items( 'id': 'cJtK9SsnJhKPyxESe7g3DG', 'cnpj': '15608435000190', 'email': 'org+15608435000190@users.noreply.betaeducacao.com.br', - 'metadata#billing_policy': 'PIX', + 'payment_method': 'PIX', 'payment_policy': {'due_days': Decimal('90')}, } diff --git a/layercake/uv.lock b/layercake/uv.lock index 10f85c1..eb87007 100644 --- a/layercake/uv.lock +++ b/layercake/uv.lock @@ -675,7 +675,7 @@ wheels = [ [[package]] name = "layercake" -version = "0.9.9" +version = "0.9.11" source = { editable = "." } dependencies = [ { name = "arnparse" },