remove prefix for partition key

This commit is contained in:
2025-03-26 15:27:26 -03:00
parent 3564119581
commit 3d6801df70
7 changed files with 68 additions and 16 deletions

View File

@@ -17,6 +17,7 @@ from layercake.dynamodb import (
KeyPair, KeyPair,
MissingError, MissingError,
PartitionKey, PartitionKey,
PrefixKey,
) )
from pydantic import UUID4, BaseModel, StringConstraints from pydantic import UUID4, BaseModel, StringConstraints
@@ -89,7 +90,7 @@ def get_emails(id: str):
start_key = router.current_event.get_query_string_value('start_key', None) start_key = router.current_event.get_query_string_value('start_key', None)
return collect.get_items( return collect.get_items(
key=KeyPair(id, 'emails'), KeyPair(id, PrefixKey('emails#')),
start_key=start_key, start_key=start_key,
) )
@@ -104,7 +105,9 @@ def get_logs(id: str):
start_key = router.current_event.get_query_string_value('start_key', None) start_key = router.current_event.get_query_string_value('start_key', None)
return collect.get_items( return collect.get_items(
key=PartitionKey(ComposeKey(id, prefix='log', delimiter=':')), # Post-migration: uncomment to enable PartitionKey with a composite key (id with `logs` prefix).
# PartitionKey(ComposeKey(id, prefix='logs')),
PartitionKey(ComposeKey(id, prefix='log', delimiter=':')),
start_key=start_key, start_key=start_key,
) )
@@ -119,6 +122,6 @@ def get_orgs(id: str):
start_key = router.current_event.get_query_string_value('start_key', None) start_key = router.current_event.get_query_string_value('start_key', None)
return collect.get_items( return collect.get_items(
key=KeyPair(id, 'orgs'), KeyPair(id, PrefixKey('orgs#')),
start_key=start_key, start_key=start_key,
) )

View File

@@ -29,7 +29,7 @@ def test_get_emails(
{ {
'email_verified': True, 'email_verified': True,
'mx_record_exists': True, 'mx_record_exists': True,
'sk': 'emails#sergio@somosbeta.com.br', 'sk': 'sergio@somosbeta.com.br',
'email_primary': True, 'email_primary': True,
'id': '5OxmMjL-ujoR5IMGegQz', 'id': '5OxmMjL-ujoR5IMGegQz',
'create_date': '2019-03-25T00:00:00-03:00', 'create_date': '2019-03-25T00:00:00-03:00',
@@ -40,6 +40,28 @@ def test_get_emails(
} }
def test_get_logs(
mock_app,
dynamodb_seeds,
dynamodb_persistence_layer: DynamoDBPersistenceLayer,
http_api_proxy: HttpApiProxy,
lambda_context: LambdaContext,
):
mock_app.users.collect = DynamoDBCollection(dynamodb_persistence_layer)
r = mock_app.lambda_handler(
http_api_proxy(
raw_path='/users/5OxmMjL-ujoR5IMGegQz/logs',
method=HTTPMethod.GET,
),
lambda_context,
)
assert r['statusCode'] == HTTPStatus.OK
print(r['body'])
def test_post_user( def test_post_user(
mock_app, mock_app,
http_api_proxy: HttpApiProxy, http_api_proxy: HttpApiProxy,

View File

@@ -4,5 +4,5 @@
{"id": {"S": "5OxmMjL-ujoR5IMGegQz"}, "sk": {"S": "acls#*"}, "create_date": {"S": "2022-06-13T15:00:24.309410-03:00"}, "roles": {"L": [{"S": "ADMIN"}]}} {"id": {"S": "5OxmMjL-ujoR5IMGegQz"}, "sk": {"S": "acls#*"}, "create_date": {"S": "2022-06-13T15:00:24.309410-03:00"}, "roles": {"L": [{"S": "ADMIN"}]}}
{"id": {"S": "5OxmMjL-ujoR5IMGegQz"}, "sk": {"S": "acls#cJtK9SsnJhKPyxESe7g3DG"}, "create_date": {"S": "2025-03-14T10:06:34.628078-03:00"}, "roles": {"L": [{"S": "ADMIN"}]}} {"id": {"S": "5OxmMjL-ujoR5IMGegQz"}, "sk": {"S": "acls#cJtK9SsnJhKPyxESe7g3DG"}, "create_date": {"S": "2025-03-14T10:06:34.628078-03:00"}, "roles": {"L": [{"S": "ADMIN"}]}}
{"id": {"S": "5OxmMjL-ujoR5IMGegQz"}, "sk": {"S": "orgs#cJtK9SsnJhKPyxESe7g3DG"}, "cnpj": {"S": "15608435000190"}, "create_date": {"S": "2025-03-13T16:36:50.073156-03:00"}, "name": {"S": "Beta Educação"}} {"id": {"S": "5OxmMjL-ujoR5IMGegQz"}, "sk": {"S": "orgs#cJtK9SsnJhKPyxESe7g3DG"}, "cnpj": {"S": "15608435000190"}, "create_date": {"S": "2025-03-13T16:36:50.073156-03:00"}, "name": {"S": "Beta Educação"}}
{"id": {"S": "logs#5OxmMjL-ujoR5IMGegQz"}, "sk": {"S": "2024-02-08T16:42:33.776409-03:00"}, "action": {"S": "OPEN_EMAIL"}} {"id": {"S": "log:5OxmMjL-ujoR5IMGegQz"}, "sk": {"S": "2024-02-08T16:42:33.776409-03:00"}, "action": {"S": "OPEN_EMAIL"}}
{"id": {"S": "logs#5OxmMjL-ujoR5IMGegQz"}, "sk": {"S": "2019-03-25T00:00:00-03:00"}, "action": {"S": "CLICK_EMAIL"}} {"id": {"S": "log:5OxmMjL-ujoR5IMGegQz"}, "sk": {"S": "2019-03-25T00:00:00-03:00"}, "action": {"S": "CLICK_EMAIL"}}

View File

@@ -669,10 +669,12 @@ class DynamoDBCollection:
items = res['items'] items = res['items']
last_key = _startkey_b64encode(res['last_key']) if res['last_key'] else None last_key = _startkey_b64encode(res['last_key']) if res['last_key'] else None
# Remove prefix from Sort Key if `key[SK]` is a PrefixKey match key.get(PK), key.get(SK):
if isinstance(key.get(SK), PrefixKey): case ComposeKey(), _: # Remove prefix from Partition Key
prefix = key[SK].prefix prefix = key[PK].prefix + key[PK].delimiter
items = [item | {SK: item[SK].removeprefix(prefix)} for item in items] items = _remove_prefix(items, PK, prefix)
case _, PrefixKey(): # Remove prefix from Sort Key
items = _remove_prefix(items, SK, key[SK].prefix)
return { return {
'items': items, 'items': items,
@@ -680,6 +682,16 @@ class DynamoDBCollection:
} }
def _remove_prefix(
items: list[dict[str, Any]],
/,
key: str,
prefix: str,
) -> list[dict[str, Any]]:
"""Remove the given prefix from the value associated with key in each item."""
return [x | {key: x[key].removeprefix(prefix)} for x in items]
def _startkey_b64encode(obj: dict) -> str: def _startkey_b64encode(obj: dict) -> str:
s = json.dumps(obj) s = json.dumps(obj)
b = urlsafe_b64encode(s.encode('utf-8')).decode('utf-8') b = urlsafe_b64encode(s.encode('utf-8')).decode('utf-8')

View File

@@ -1,6 +1,6 @@
[project] [project]
name = "layercake" name = "layercake"
version = "0.1.1" version = "0.1.2"
description = "Add your description here" description = "Add your description here"
readme = "README.md" readme = "README.md"
authors = [ authors = [

View File

@@ -171,18 +171,33 @@ def test_collection_get_items(
collect = DynamoDBCollection(dynamodb_persistence_layer) collect = DynamoDBCollection(dynamodb_persistence_layer)
# This data was added from seeds # This data was added from seeds
data = collect.get_items( logs = collect.get_items(
PartitionKey( PartitionKey(
ComposeKey('5OxmMjL-ujoR5IMGegQz', prefix='logs'), ComposeKey('5OxmMjL-ujoR5IMGegQz', prefix='logs'),
), ),
) )
assert len(data['items']) == 2 assert len(logs['items']) == 2
assert logs == {
'items': [
{
'sk': '2024-02-08T16:42:33.776409-03:00',
'action': 'OPEN_EMAIL',
'id': '5OxmMjL-ujoR5IMGegQz',
},
{
'sk': '2019-03-25T00:00:00-03:00',
'action': 'CLICK_EMAIL',
'id': '5OxmMjL-ujoR5IMGegQz',
},
],
'last_key': None,
}
# This data was added from seeds # This data was added from seeds
data = collect.get_items( emails = collect.get_items(
KeyPair('5OxmMjL-ujoR5IMGegQz', PrefixKey('emails#')), KeyPair('5OxmMjL-ujoR5IMGegQz', PrefixKey('emails#')),
) )
assert data == { assert emails == {
'items': [ 'items': [
{ {
'email_verified': True, 'email_verified': True,

2
layercake/uv.lock generated
View File

@@ -417,7 +417,7 @@ wheels = [
[[package]] [[package]]
name = "layercake" name = "layercake"
version = "0.1.0" version = "0.1.1"
source = { editable = "." } source = { editable = "." }
dependencies = [ dependencies = [
{ name = "aws-lambda-powertools", extra = ["all"] }, { name = "aws-lambda-powertools", extra = ["all"] },