add cert_expires_at

This commit is contained in:
2025-10-15 15:10:47 -03:00
parent 54c92b3996
commit ffa04d9b15
37 changed files with 371 additions and 230 deletions

View File

@@ -253,7 +253,6 @@ class KeyPair(Key):
sk: str,
*,
rename_key: str | None = None,
retain_key: bool = False,
table_name: str | None = None,
) -> None:
"""
@@ -267,24 +266,17 @@ class KeyPair(Key):
The sort key.
rename_key : str, optional
If provided, renames the sort key in the output.
retain_key : bool, optional
Use the key itself as value if True; otherwise, use the extracted value.
table_name : str, optional
"""
super().__init__(**{PK: pk, SK: sk})
self._rename_key = rename_key
self._retain_key = retain_key
self._table_name = table_name
@property
def rename_key(self) -> str | None:
return self._rename_key
@property
def retain_key(self) -> bool:
return self._retain_key
@property
def table_name(self) -> str | None:
return self._table_name
@@ -435,14 +427,11 @@ class TransactWriter:
if cond_expr:
attrs['ConditionExpression'] = cond_expr
if not table_name:
table_name = self._table_name
self._add_op_and_process(
TransactOperation(
{
'Put': dict(
TableName=table_name,
TableName=table_name or self._table_name,
Item=serialize(item),
**attrs,
)
@@ -473,14 +462,11 @@ class TransactWriter:
if expr_attr_values:
attrs['ExpressionAttributeValues'] = serialize(expr_attr_values)
if not table_name:
table_name = self._table_name
self._add_op_and_process(
TransactOperation(
{
'Update': dict(
TableName=table_name,
TableName=table_name or self._table_name,
Key=serialize(key),
UpdateExpression=update_expr,
**attrs,
@@ -511,14 +497,11 @@ class TransactWriter:
if expr_attr_values:
attrs['ExpressionAttributeValues'] = serialize(expr_attr_values)
if not table_name:
table_name = self._table_name
self._add_op_and_process(
TransactOperation(
{
'Delete': dict(
TableName=table_name,
TableName=table_name or self._table_name,
Key=serialize(key),
**attrs,
)
@@ -545,14 +528,11 @@ class TransactWriter:
if expr_attr_values:
attrs['ExpressionAttributeValues'] = serialize(expr_attr_values)
if not table_name:
table_name = self._table_name
self._add_op_and_process(
TransactOperation(
{
'ConditionCheck': dict(
TableName=table_name,
TableName=table_name or self._table_name,
Key=serialize(key),
**attrs,
)
@@ -619,6 +599,7 @@ class DynamoDBPersistenceLayer:
filter_expr: str | None = None,
limit: int | None = None,
index_forward: bool = True,
table_name: str | None = None,
) -> dict[str, Any]:
"""You must provide the name of the partition key attribute
and a single value for that attribute.
@@ -637,7 +618,7 @@ class DynamoDBPersistenceLayer:
- https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dynamodb/client/query.html
"""
attrs: dict = {
'TableName': self.table_name,
'TableName': table_name or self.table_name,
'KeyConditionExpression': key_cond_expr,
'ScanIndexForward': index_forward,
}
@@ -658,18 +639,18 @@ class DynamoDBPersistenceLayer:
attrs['Limit'] = limit
try:
response = self.client.query(**attrs)
r = self.client.query(**attrs)
except ClientError as err:
logger.info(attrs)
logger.exception(err)
raise
else:
return dict(
items=[deserialize(v) for v in response.get('Items', [])],
last_key=response.get('LastEvaluatedKey', None),
items=[deserialize(v) for v in r.get('Items', [])],
last_key=r.get('LastEvaluatedKey', None),
)
def get_item(self, key: dict) -> dict:
def get_item(self, key: dict, table_name: str | None = None) -> dict:
"""The GetItem operation returns a set of attributes for the item
with the given primary key.
@@ -677,22 +658,28 @@ class DynamoDBPersistenceLayer:
there will be no Item element in the response.
"""
attrs = {
'TableName': self.table_name,
'TableName': table_name or self.table_name,
'Key': serialize(key),
}
try:
response = self.client.get_item(**attrs)
r = self.client.get_item(**attrs)
except ClientError as err:
logger.info(attrs)
logger.exception(err)
raise
else:
return deserialize(response.get('Item', {}))
return deserialize(r.get('Item', {}))
def put_item(self, item: dict, *, cond_expr: str | None = None) -> bool:
def put_item(
self,
item: dict,
*,
cond_expr: str | None = None,
table_name: str | None = None,
) -> bool:
attrs = {
'TableName': self.table_name,
'TableName': table_name or self.table_name,
'Item': serialize(item),
}
@@ -716,9 +703,10 @@ class DynamoDBPersistenceLayer:
cond_expr: str | None = None,
expr_attr_names: dict | None = None,
expr_attr_values: dict | None = None,
table_name: str | None = None,
) -> bool:
attrs: dict = {
'TableName': self.table_name,
'TableName': table_name or self.table_name,
'Key': serialize(key),
'UpdateExpression': update_expr,
}
@@ -748,13 +736,14 @@ class DynamoDBPersistenceLayer:
cond_expr: str | None = None,
expr_attr_names: dict | None = None,
expr_attr_values: dict | None = None,
table_name: str | None = None,
) -> bool:
"""Deletes a single item in a table by primary key. You can perform
a conditional delete operation that deletes the item if it exists,
or if it has an expected attribute value.
"""
attrs: dict = {
'TableName': self.table_name,
'TableName': table_name or self.table_name,
'Key': serialize(key),
}
@@ -780,9 +769,13 @@ class DynamoDBPersistenceLayer:
def collection(self) -> 'DynamoDBCollection':
return DynamoDBCollection(self)
def transact_writer(self, flush_amount: int = 50) -> TransactWriter:
def transact_writer(
self,
flush_amount: int = 50,
table_name: str | None = None,
) -> TransactWriter:
return TransactWriter(
table_name=self.table_name,
table_name=table_name or self.table_name,
client=self.client,
flush_amount=flush_amount,
)
@@ -913,19 +906,20 @@ class DynamoDBCollection:
Raises the provided exception if the item is not found
and raise_on_error is True.
"""
exc_cls = exc_cls or self.exc_cls
data = self.persistence_layer.get_item(key)
table_name = getattr(key, 'table_name', None)
path_spec = getattr(key[SK], 'path_spec', None)
r = self.persistence_layer.get_item(key, table_name)
if raise_on_error and not data:
if raise_on_error and not r:
exc_cls = exc_cls or self.exc_cls
raise exc_cls(f'Item with {key} not found.')
if path_spec and data:
if path_spec and r:
from glom import glom
return glom(data, path_spec, default=default)
return glom(r, path_spec, default=default)
return data or default
return r or default
def put_item(
self,
@@ -954,6 +948,8 @@ class DynamoDBCollection:
bool
True if the operation is successful, False otherwise.
"""
table_name = getattr(key, 'table_name', None)
if isinstance(ttl, int):
kwargs.update({'ttl': ttl})
@@ -963,6 +959,7 @@ class DynamoDBCollection:
return self.persistence_layer.put_item(
item=key | kwargs,
cond_expr=cond_expr,
table_name=table_name,
)
def delete_item(
@@ -991,11 +988,14 @@ class DynamoDBCollection:
bool
True if the item is successfully deleted, False otherwise.
"""
table_name = getattr(key, 'table_name', None)
return self.persistence_layer.delete_item(
key=key,
cond_expr=cond_expr,
expr_attr_names=expr_attr_names,
expr_attr_values=expr_attr_values,
table_name=table_name,
)
def get_items(
@@ -1059,7 +1059,7 @@ class DynamoDBCollection:
if not key.pairs:
return {}
sortkeys = key.pairs[1:] if flatten_top else key.pairs
pairs = key.pairs[1:] if flatten_top else key.pairs
client = self.persistence_layer.client
table_name = self.persistence_layer.table_name
@@ -1074,8 +1074,8 @@ class DynamoDBCollection:
for pair in key.pairs
]
response = client.transact_get_items(TransactItems=transact_items) # type: ignore
items = [deserialize(r.get('Item', {})) for r in response.get('Responses', [])]
r = client.transact_get_items(TransactItems=transact_items) # type: ignore
items = [deserialize(r.get('Item', {})) for r in r.get('Responses', [])]
if flatten_top:
head, *tail = items
@@ -1103,16 +1103,14 @@ class DynamoDBCollection:
if getattr(sk, 'rename_key', None):
return sk.rename_key
if not isinstance(sk, SortKey):
return pk
if isinstance(sk, SortKey):
return sk.removeprefix(sk.remove_prefix or '')
key = pk if pair.retain_key else sk
return key.removeprefix(sk.remove_prefix or '')
return pk
return head | {
_map_key(pair): _extract_sk_values(pair, obj)
for pair, obj in zip(sortkeys, tail)
for pair, obj in zip(pairs, tail)
if obj
}
@@ -1179,7 +1177,7 @@ class DynamoDBCollection:
else '#pk = :pk'
)
response = self.persistence_layer.query(
r = self.persistence_layer.query(
key_cond_expr=key_cond_expr,
expr_attr_name=key.expr_attr_name() | expr_attr_name,
expr_attr_values=key.expr_attr_values() | expr_attr_values,
@@ -1189,10 +1187,8 @@ class DynamoDBCollection:
start_key=_startkey_b64decode(start_key) if start_key else {},
)
items = response['items']
last_key = (
_startkey_b64encode(response['last_key']) if response['last_key'] else None
)
items = r['items']
last_key = _startkey_b64encode(r['last_key']) if r['last_key'] else None
def _removeprefix(
items: list[dict[str, Any]], /, key: str, prefix: str

View File

@@ -1,6 +1,6 @@
[project]
name = "layercake"
version = "0.10.1"
version = "0.11.0"
description = "Packages shared dependencies to optimize deployment and ensure consistency across functions."
readme = "README.md"
authors = [

View File

@@ -178,7 +178,7 @@ def test_collection_get_item_path_spec(
KeyPair(
pk='5OxmMjL-ujoR5IMGegQz',
sk=SortKey(
ComposeKey('sergio@somosbeta.com.br', prefix='emails'),
'emails#sergio@somosbeta.com.br',
path_spec='mx_record_exists',
),
),
@@ -193,7 +193,7 @@ def test_collection_put_item(
assert dynamodb_persistence_layer.collection.put_item(
KeyPair(
'5OxmMjL-ujoR5IMGegQz',
ComposeKey('6d1044d5-18c5-437c-9219-fc2ace7e5ebc', prefix='orgs'),
'orgs#6d1044d5-18c5-437c-9219-fc2ace7e5ebc',
),
name='Beta Educação',
ttl=ttl(days=3),
@@ -202,7 +202,7 @@ def test_collection_put_item(
data = dynamodb_persistence_layer.collection.get_item(
KeyPair(
pk='5OxmMjL-ujoR5IMGegQz',
sk=ComposeKey('6d1044d5-18c5-437c-9219-fc2ace7e5ebc', prefix='orgs'),
sk='orgs#6d1044d5-18c5-437c-9219-fc2ace7e5ebc',
),
)
@@ -219,7 +219,7 @@ def test_collection_delete_item(
assert dynamodb_persistence_layer.collection.delete_item(
KeyPair(
'5OxmMjL-ujoR5IMGegQz',
ComposeKey('sergio@somsbeta.com.br', prefix='emails'),
'emails#sergio@somsbeta.com.br',
)
)
@@ -232,6 +232,7 @@ def test_collection_query(
logs = dynamodb_persistence_layer.collection.query(
PartitionKey(
ComposeKey('5OxmMjL-ujoR5IMGegQz', prefix='logs'),
# 'logs#5OxmMjL-ujoR5IMGegQz',
),
)
assert len(logs['items']) == 2
@@ -280,7 +281,6 @@ def test_collection_get_items(
'cJtK9SsnJhKPyxESe7g3DG', table_name=dynamodb_persistence_layer.table_name
)
+ SortKey('0')
+ SortKey('0')
+ SortKey(
'metadata#billing_policy',
path_spec='payment_method',
@@ -407,7 +407,7 @@ def test_collection_get_items_pair_path_spec(
+ KeyPair(
'email',
SortKey('osergiosiqueira@gmail.com', path_spec='user_id'),
retain_key=True,
rename_key='email',
),
flatten_top=False,
)

2
layercake/uv.lock generated
View File

@@ -675,7 +675,7 @@ wheels = [
[[package]]
name = "layercake"
version = "0.10.0"
version = "0.10.1"
source = { editable = "." }
dependencies = [
{ name = "arnparse" },