fix enrollment

This commit is contained in:
2025-05-30 20:27:00 -03:00
parent 47b3381108
commit 957f9c4a72
11 changed files with 76 additions and 35 deletions

View File

@@ -1,20 +1,36 @@
import json
import os
from datetime import date
from functools import partial
from typing import Any
from aws_lambda_powertools import Logger, Tracer
from aws_lambda_powertools.event_handler.api_gateway import (
APIGatewayHttpResolver,
CORSConfig,
Response,
content_types,
)
from aws_lambda_powertools.event_handler.exceptions import ServiceError
from aws_lambda_powertools.logging import correlation_paths
from aws_lambda_powertools.utilities.typing import LambdaContext
from api_gateway import JSONResponse
from middlewares import AuthenticationMiddleware
from routes import courses, enrollments, lookup, orders, orgs, settings, users, webhooks
class JSONEncoder(json.JSONEncoder):
def default(self, o):
if isinstance(o, date):
return o.isoformat()
return super().default(o)
def foo(obj):
print(obj)
return json.dumps(obj, separators=(',', ':'), cls=JSONEncoder)
tracer = Tracer()
logger = Logger(__name__)
cors = CORSConfig(
@@ -27,6 +43,7 @@ app = APIGatewayHttpResolver(
enable_validation=True,
cors=cors,
debug='AWS_SAM_LOCAL' in os.environ,
serializer=partial(json.dumps, separators=(',', ':'), cls=JSONEncoder),
)
app.use(middlewares=[AuthenticationMiddleware()])
app.include_router(courses.router, prefix='/courses')
@@ -47,12 +64,11 @@ app.include_router(lookup.router, prefix='/lookup')
@app.exception_handler(ServiceError)
def exc_error(exc: ServiceError):
return Response(
return JSONResponse(
body={
'msg': exc.msg,
'err': type(exc).__name__,
'type': type(exc).__name__,
'message': str(exc),
},
content_type=content_types.APPLICATION_JSON,
status_code=exc.status_code,
)

View File

@@ -51,18 +51,9 @@ def enroll_(payload: Payload):
context = {'tenant': router.context['tenant']}
with processor(payload.items, handler, context):
processor.process()
processed_messages = processor.process()
print(processor.exceptions)
return JSONResponse(
HTTPStatus.OK,
{
'successes': processor.successes,
'failures': processor.failures,
'exceptions': [str(exc) for exc in processor.exceptions],
},
)
return JSONResponse(HTTPStatus.OK, processed_messages)
def handler(record: Item, context: dict):

View File

@@ -63,6 +63,7 @@ def enroll(
user = enrollment.user
course = enrollment.course
tenant_id = tenant['id']
lock_hash = md5_hash('%s%s' % (user.id, course.id))
with persistence_layer.transact_writer() as transact:
transact.put(
@@ -117,19 +118,24 @@ def enroll(
},
)
class DeduplicationConflictError(Exception):
def __init__(self, *args):
super().__init__('Enrollment already exists')
# Prevents the user from enrolling in the same course again until
# the deduplication window expires or is removed
transact.condition(
key=KeyPair('lock', lock_hash),
cond_expr='attribute_not_exists(sk)',
exc_cls=DeduplicationConflictError,
)
if deduplication_window:
lock_hash = md5_hash('%s%s' % (user.id, course.id))
offset_days = deduplication_window['offset_days']
ttl_expiration = ttl(
start_dt=now_ + timedelta(days=course.access_period - offset_days)
)
class DeduplicationConflictError(Exception):
def __init__(self, *args):
super().__init__('Enrollment already exists')
transact.put(
item={
'id': 'lock',
@@ -138,8 +144,6 @@ def enroll(
'create_date': now_,
'ttl': ttl_expiration,
},
cond_expr='attribute_not_exists(sk)',
exc_cls=DeduplicationConflictError,
)
transact.put(
item={

View File

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

View File

@@ -26,6 +26,7 @@ def test_enroll(
headers={'X-Tenant': 'cJtK9SsnJhKPyxESe7g3DG'},
body={
'items': [
# existing enrollment, must fail
{
'user': {
'id': '5OxmMjL-ujoR5IMGegQz',
@@ -37,9 +38,6 @@ def test_enroll(
'id': '6d69a34a-cefd-40aa-a89b-dceb694c3e61',
'name': 'pytest',
},
'deduplication_window': {
'offset_days': 60,
},
},
{
'user': {
@@ -52,6 +50,9 @@ def test_enroll(
'id': '6d69a34a-cefd-40aa-a89b-dceb694c3e61',
'name': 'pytest',
},
'deduplication_window': {
'offset_days': 60,
},
},
],
},
@@ -59,8 +60,13 @@ def test_enroll(
lambda_context,
)
# assert r['statusCode'] == HTTPStatus.OK
print(json.loads(r['body']))
assert r['statusCode'] == HTTPStatus.OK
fail, _ = json.loads(r['body'])
assert fail['cause'] == {
'type': 'DeduplicationConflictError',
'message': 'Enrollment already exists',
}
def test_vacancies(

2
http-api/uv.lock generated
View File

@@ -560,7 +560,7 @@ wheels = [
[[package]]
name = "layercake"
version = "0.6.2"
version = "0.6.5"
source = { directory = "../layercake" }
dependencies = [
{ name = "arnparse" },