rename params

This commit is contained in:
2025-03-27 21:35:36 -03:00
parent 5756451738
commit f757334899
12 changed files with 53 additions and 41 deletions

View File

@@ -1,4 +1,4 @@
class UserNotFound(Exception): ...
class UnauthorizedError(Exception): ...
def get_user(access_token: str, *, idp_client) -> dict | None:
@@ -6,6 +6,6 @@ def get_user(access_token: str, *, idp_client) -> dict | None:
try:
user = idp_client.get_user(AccessToken=access_token)
except idp_client.exceptions.ClientError:
raise UserNotFound()
raise UnauthorizedError()
else:
return {attr['Name']: attr['Value'] for attr in user['UserAttributes']}

View File

@@ -1,5 +1,3 @@
import json
from aws_lambda_powertools.event_handler.api_gateway import (
APIGatewayHttpResolver,
Response,
@@ -8,7 +6,9 @@ from aws_lambda_powertools.event_handler.middlewares import (
BaseMiddlewareHandler,
NextMiddleware,
)
from aws_lambda_powertools.shared.json_encoder import Encoder
from aws_lambda_powertools.shared.functions import (
extract_event_from_common_models,
)
from layercake.dateutils import now, ttl
from layercake.dynamodb import ComposeKey, DynamoDBCollection, KeyPair
from pydantic import UUID4, BaseModel, Field
@@ -16,6 +16,14 @@ from pydantic import UUID4, BaseModel, Field
LOG_RETENTION_DAYS = 365 * 2 # 2 years
class AuthenticatedUser(BaseModel):
id: str = Field(alias='custom:user_id')
name: str
email: str
email_verified: bool
sub: UUID4
class AuthorizerMiddleware(BaseMiddlewareHandler):
def handler(
self,
@@ -33,14 +41,6 @@ class AuthorizerMiddleware(BaseMiddlewareHandler):
return next_middleware(app)
class AuthenticatedUser(BaseModel):
id: str = Field(alias='custom:user_id')
name: str
email: str
email_verified: bool
sub: UUID4
class AuditLogMiddleware(BaseMiddlewareHandler):
"""This middleware logs audit details for successful requests, storing user,
action, and IP info with a specified retention period.."""
@@ -71,7 +71,9 @@ class AuditLogMiddleware(BaseMiddlewareHandler):
if 200 <= response.status_code < 300 and user:
now_ = now()
data = (
json.dumps(response.body, cls=Encoder) if response.is_json() else None
extract_event_from_common_models(response.body)
if response.is_json()
else None
)
retention_days = (
ttl(start_dt=now_, days=self.retention_days)

View File

@@ -53,7 +53,7 @@ def post_course(payload: Course):
)
return Response(
body={'id': str(payload.id), },
body=payload,
content_type=content_types.APPLICATION_JSON,
status_code=HTTPStatus.CREATED,
)

View File

@@ -23,6 +23,7 @@ from pydantic import UUID4, BaseModel, StringConstraints
import elastic
from boto3clients import dynamodb_client
from middlewares import AuditLogMiddleware
from models import User
from settings import ELASTIC_CONN, USER_TABLE
@@ -32,7 +33,7 @@ class BadRequestError(MissingError, PowertoolsBadRequestError): ...
router = Router()
user_layer = DynamoDBPersistenceLayer(USER_TABLE, dynamodb_client)
collect = DynamoDBCollection(user_layer, exc_cls=BadRequestError)
collect = DynamoDBCollection(user_layer, exception_cls=BadRequestError)
elastic_client = Elasticsearch(**ELASTIC_CONN)
@@ -60,6 +61,7 @@ def get_users():
compress=True,
tags=['User'],
summary='Create user',
middlewares=[AuditLogMiddleware('USER_ADD', collect)],
)
def post_user(payload: User):
return Response(status_code=HTTPStatus.CREATED)

View File

@@ -23,7 +23,7 @@ Globals:
Architectures:
- x86_64
Layers:
- !Sub arn:aws:lambda:sa-east-1:336641857101:layer:layercake:25
- !Sub arn:aws:lambda:sa-east-1:336641857101:layer:layercake:26
Environment:
Variables:
TZ: America/Sao_Paulo

View File

@@ -13,6 +13,11 @@ PK = os.getenv('DYNAMODB_PARTITION_KEY', 'pk')
SK = os.getenv('DYNAMODB_SORT_KEY', 'sk')
patch = pytest.MonkeyPatch()
patch.setenv('USER_TABLE', PYTEST_TABLE_NAME)
patch.setenv('COURSE_TABLE', PYTEST_TABLE_NAME)
@dataclass
class LambdaContext:
function_name: str = 'test'
@@ -137,9 +142,6 @@ def dynamodb_seeds(dynamodb_client):
@pytest.fixture
def mock_app(monkeypatch):
for table_name in ['USER_TABLE', 'COURSE_TABLE']:
monkeypatch.setenv(table_name, PYTEST_TABLE_NAME)
import app
return app

View File

@@ -87,6 +87,7 @@ def test_get_logs(
def test_post_user(
mock_app,
dynamodb_client,
http_api_proxy: HttpApiProxy,
lambda_context: LambdaContext,
):

View File

@@ -5,6 +5,8 @@ from .conftest import LambdaContext
def test_bearer_jwt(lambda_context: LambdaContext):
import auth as app
# You should mock the Cognito user to pass the test
app.get_user = lambda *args, **kwargs: {
'sub': '58efed8d-d276-41a8-8502-4ab8b5a6415e',
@@ -30,6 +32,7 @@ def test_bearer_jwt(lambda_context: LambdaContext):
def test_bearer_apikey(
monkeypatch,
dynamodb_seeds,
lambda_context: LambdaContext,
):
@@ -38,6 +41,7 @@ def test_bearer_apikey(
'authorization': 'Bearer sk-MzI1MDQ0NTctZjEzMy00YzAwLTkzNmItNmFhNzEyY2E5ZjQw',
}
}
# This data was added from seeds
assert app.lambda_handler(event, lambda_context) == {
'isAuthorized': True,
@@ -49,7 +53,7 @@ def test_bearer_apikey(
},
}
# This data was added from seeds
# # This data was added from seeds
assert app.lambda_handler(
{
'headers': {

2
http-api/uv.lock generated
View File

@@ -444,7 +444,7 @@ wheels = [
[[package]]
name = "layercake"
version = "0.1.8"
version = "0.1.9"
source = { directory = "../layercake" }
dependencies = [
{ name = "aws-lambda-powertools", extra = ["all"] },

View File

@@ -27,30 +27,31 @@ serializer = TypeSerializer()
deserializer = TypeDeserializer()
def _serialize_python_type(value: Any) -> str | dict | list:
match value:
def _serialize_to_primitive_types(data: Any) -> str | dict | list:
match data:
case datetime():
return value.isoformat()
return data.isoformat()
case UUID():
return str(value)
return str(data)
case IPv4Address():
return str(value)
return str(data)
case list() | tuple():
return [_serialize_python_type(v) for v in value]
return [_serialize_to_primitive_types(v) for v in data]
case dict():
return {k: _serialize_python_type(v) for k, v in value.items()}
return {k: _serialize_to_primitive_types(v) for k, v in data.items()}
case _:
return value
return data
def serialize(value: dict) -> dict:
def serialize(data: dict) -> dict:
return {
k: serializer.serialize(_serialize_python_type(v)) for k, v in value.items()
k: serializer.serialize(_serialize_to_primitive_types(v))
for k, v in data.items()
}
def deserialize(value: dict) -> dict:
return {k: deserializer.deserialize(v) for k, v in value.items()}
def deserialize(data: dict) -> dict:
return {k: deserializer.deserialize(v) for k, v in data.items()}
if TYPE_CHECKING:
@@ -555,16 +556,16 @@ class DynamoDBCollection:
self,
persistence_layer: DynamoDBPersistenceLayer,
/,
exc_cls: Type[ValueError] = MissingError,
exception_cls: Type[ValueError] = MissingError,
tz: str = TZ,
) -> None:
if not issubclass(exc_cls, ValueError):
if not issubclass(exception_cls, ValueError):
raise TypeError(
f'exception_cls must be a subclass of ValueError, got {exc_cls}'
f'exception_cls must be a subclass of ValueError, got {exception_cls}'
)
self.persistence_layer = persistence_layer
self.exc_cls = exc_cls
self.exception_cls = exception_cls
self.tz = tz
def get_item(
@@ -575,7 +576,7 @@ class DynamoDBCollection:
default: Any = None,
delimiter: str = '#',
) -> Any:
exc_cls = self.exc_cls
exc_cls = self.exception_cls
data = self.persistence_layer.get_item(key)
if raise_on_error and not data:

View File

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

2
layercake/uv.lock generated
View File

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