update batch

This commit is contained in:
2025-05-30 15:32:31 -03:00
parent efd962cdba
commit b048febbd5
24 changed files with 455 additions and 69 deletions

View File

@@ -24,6 +24,7 @@ Example
"""
from dataclasses import asdict, dataclass
from enum import Enum
from typing import Any
from aws_lambda_powertools import Logger, Tracer
@@ -33,7 +34,6 @@ from aws_lambda_powertools.utilities.data_classes.api_gateway_authorizer_event i
APIGatewayAuthorizerResponseV2,
)
from aws_lambda_powertools.utilities.typing import LambdaContext
from botocore.endpoint_provider import Enum
from layercake.dynamodb import DynamoDBCollection, DynamoDBPersistenceLayer, KeyPair
from layercake.funcs import pick

View File

@@ -4,12 +4,12 @@ import boto3
def get_dynamodb_client():
sam_local = os.getenv('AWS_SAM_LOCAL')
running_sam_local = os.getenv('AWS_SAM_LOCAL')
if os.getenv('AWS_LAMBDA_FUNCTION_NAME') and not sam_local:
if os.getenv('AWS_LAMBDA_FUNCTION_NAME') and not running_sam_local:
return boto3.client('dynamodb')
url = 'host.docker.internal' if sam_local else 'localhost'
url = 'host.docker.internal' if running_sam_local else 'localhost'
return boto3.client('dynamodb', endpoint_url=f'http://{url}:8000')

View File

@@ -40,7 +40,7 @@ class Course(BaseModel):
class Enrollment(BaseModel):
id: UUID4 = Field(default_factory=uuid4)
id: UUID4 | str = Field(default_factory=uuid4)
user: User
course: Course
progress: int = Field(default=0, ge=0, le=100)

View File

@@ -46,7 +46,10 @@ def cancel(id: str, payload: Cancel):
set_status_as_canceled(
id,
lock_hash=payload.lock_hash,
author=user.model_dump(), # type: ignore
author={
'id': user.id,
'name': user.name,
},
course=payload.course, # type: ignore
vacancy_key=KeyPair.parse_obj(payload.vacancy),
persistence_layer=enrollment_layer,

View File

@@ -1,4 +1,5 @@
from datetime import datetime
from http import HTTPStatus
from aws_lambda_powertools.event_handler.api_gateway import Router
from layercake.batch import BatchProcessor
@@ -8,13 +9,15 @@ from layercake.dynamodb import (
)
from pydantic import BaseModel
from api_gateway import JSONResponse
from boto3clients import dynamodb_client
from config import (
ENROLLMENT_TABLE,
USER_TABLE,
)
from middlewares import Tenant, TenantMiddleware
from models import Course, User
from models import Course, Enrollment, User
from rules.enrollment import enroll
router = Router()
@@ -28,6 +31,7 @@ processor = BatchProcessor()
class Item(BaseModel):
user: User
course: Course
deduplication_window: dict = {}
schedule_date: datetime | None = None
@@ -49,16 +53,33 @@ def enroll_(payload: Payload):
with processor(payload.items, handler, context):
processor.process()
return {}
print(processor.exceptions)
return JSONResponse(
HTTPStatus.OK,
{
'successes': processor.successes,
'failures': processor.failures,
'exceptions': [str(exc) for exc in processor.exceptions],
},
)
def handler(record: Item, context: dict):
tenant: Tenant = context['tenant']
# enroll(
# enrollment=Enrollment(user=[])
# tenant={
# 'id': str(tenant.id),
# 'name': tenant.name,
# },
# persistence_layer=enrollment_layer,
# )
enrollment = Enrollment(
user=record.user,
course=record.course,
)
enroll(
enrollment=enrollment,
tenant={
'id': str(tenant.id),
'name': tenant.name,
},
deduplication_window=record.deduplication_window, # type: ignore
persistence_layer=enrollment_layer,
)
return enrollment

View File

@@ -125,6 +125,11 @@ def enroll(
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',
@@ -134,6 +139,7 @@ def enroll(
'ttl': ttl_expiration,
},
cond_expr='attribute_not_exists(sk)',
exc_cls=DeduplicationConflictError,
)
transact.put(
item={

View File

@@ -4,7 +4,6 @@ from typing import TypedDict
from aws_lambda_powertools.event_handler.exceptions import (
BadRequestError,
)
from botocore.tokens import timedelta
from layercake.dateutils import now, ttl
from layercake.dynamodb import (
ComposeKey,

View File

@@ -8,6 +8,7 @@ dependencies = ["layercake"]
[dependency-groups]
dev = [
"boto3-stubs[essential]>=1.38.26",
"jsonlines>=4.0.0",
"pytest>=8.3.4",
"pytest-cov>=6.0.0",

View File

@@ -1,3 +1,4 @@
import json
from http import HTTPMethod, HTTPStatus
from layercake.dynamodb import (
@@ -36,6 +37,21 @@ def test_enroll(
'id': '6d69a34a-cefd-40aa-a89b-dceb694c3e61',
'name': 'pytest',
},
'deduplication_window': {
'offset_days': 60,
},
},
{
'user': {
'id': '9a41e867-55e1-4573-bd27-7b5d1d1bcfde',
'name': 'Tiago Maciel',
'email': 'tiago@somosbeta.com.br',
'cpf': '08679004901',
},
'course': {
'id': '6d69a34a-cefd-40aa-a89b-dceb694c3e61',
'name': 'pytest',
},
},
],
},
@@ -43,8 +59,8 @@ def test_enroll(
lambda_context,
)
assert r['statusCode'] == HTTPStatus.OK
print(r)
# assert r['statusCode'] == HTTPStatus.OK
print(json.loads(r['body']))
def test_vacancies(

View File

@@ -21,3 +21,4 @@
{"id": {"S": "email"}, "sk": {"S": "sergio@somosbeta.com.br"}}
{"id": {"S": "cpf"}, "sk": {"S": "07879819908"}}
{"id": {"S": "cpf"}, "sk": {"S": "08679004901"}}
{"id": {"S": "lock"}, "sk": {"S": "c2116a43f8f1aed659a10c83dab17ed3"}}

119
http-api/uv.lock generated
View File

@@ -103,6 +103,30 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/6e/98/bac2404ff6183e1aaeebfefe6f345d63a1395b9a710be5ad24dcad9538ed/boto3-1.37.20-py3-none-any.whl", hash = "sha256:225dbc75d79816cb9b28cc74a63c9fa0f2d70530d603dacd82634f362f6679c1", size = 139561, upload-time = "2025-03-25T19:21:44.723Z" },
]
[[package]]
name = "boto3-stubs"
version = "1.38.26"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "botocore-stubs" },
{ name = "types-s3transfer" },
]
sdist = { url = "https://files.pythonhosted.org/packages/1e/89/9e2658210ac11459405ed8c82f47be533a5d16ae3b8203c90564b7a738a0/boto3_stubs-1.38.26.tar.gz", hash = "sha256:492e59e42323de43018ffa6d00d3bb2b93d1fead042e76c6a68fd0a0c0fe3236", size = 99065, upload-time = "2025-05-29T19:47:49.383Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/48/51/881ac3c7ebeefdfe0e712b1f4d815261707a492187ea301506168cf6fc20/boto3_stubs-1.38.26-py3-none-any.whl", hash = "sha256:3022b2a8f6925c60c9ce68c5e090ff9fd2bad0c918300395a1c242681a67c11c", size = 68669, upload-time = "2025-05-29T19:47:42.352Z" },
]
[package.optional-dependencies]
essential = [
{ name = "mypy-boto3-cloudformation" },
{ name = "mypy-boto3-dynamodb" },
{ name = "mypy-boto3-ec2" },
{ name = "mypy-boto3-lambda" },
{ name = "mypy-boto3-rds" },
{ name = "mypy-boto3-s3" },
{ name = "mypy-boto3-sqs" },
]
[[package]]
name = "botocore"
version = "1.37.20"
@@ -117,6 +141,18 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/2d/5b/f96cf58c37704b907ac2f9cc94e45ba0a2aa3b2062421aa8b8614f1d78de/botocore-1.37.20-py3-none-any.whl", hash = "sha256:c34f4f25fda7c4f726adf5a948590bd6bd7892c05278d31e344b5908e7b43301", size = 13432464, upload-time = "2025-03-25T19:21:28.115Z" },
]
[[package]]
name = "botocore-stubs"
version = "1.38.26"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "types-awscrt" },
]
sdist = { url = "https://files.pythonhosted.org/packages/08/f2/fd2f5a8ef00bbcfe00c12b8c49e247510266929dff5578b6fec360967a21/botocore_stubs-1.38.26.tar.gz", hash = "sha256:3bbf7662fc97e28a50dc959752619cf57029194987268b4dc13df4e54767204c", size = 42315, upload-time = "2025-05-29T20:18:25.22Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/28/4a/f99ef1ff37620c0c23aa67e3d9de5fce0f98b38fd26e6d30438ee440c0fc/botocore_stubs-1.38.26-py3-none-any.whl", hash = "sha256:c86ac7d2c7e24ea50a866a9686a293dfe8b40281cc3465d79e2e0e48d35ad93b", size = 65628, upload-time = "2025-05-29T20:18:23.125Z" },
]
[[package]]
name = "brotli"
version = "1.1.0"
@@ -450,6 +486,7 @@ dependencies = [
[package.dev-dependencies]
dev = [
{ name = "boto3-stubs", extra = ["essential"] },
{ name = "jsonlines" },
{ name = "pytest" },
{ name = "pytest-cov" },
@@ -462,6 +499,7 @@ requires-dist = [{ name = "layercake", directory = "../layercake" }]
[package.metadata.requires-dev]
dev = [
{ name = "boto3-stubs", extras = ["essential"], specifier = ">=1.38.26" },
{ name = "jsonlines", specifier = ">=4.0.0" },
{ name = "pytest", specifier = ">=8.3.4" },
{ name = "pytest-cov", specifier = ">=6.0.0" },
@@ -584,6 +622,69 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/e0/2f/264c07a3f488260ea36c78cbc201b76e6baf9ef92e0c7f78657a6a5e5f22/meilisearch-0.34.0-py3-none-any.whl", hash = "sha256:fae8ad2a15d12c27fa0a1fff2ae2e4e3e2e22b869950408d63c87e2c095a9f61", size = 24373, upload-time = "2025-02-18T05:50:32.73Z" },
]
[[package]]
name = "mypy-boto3-cloudformation"
version = "1.38.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/40/d5/35b9301c8b2fb870e58401d13fec36de2c83f2ddef48398b8c89c9a58995/mypy_boto3_cloudformation-1.38.0.tar.gz", hash = "sha256:563399166c07e91e0695fb1e58103a248b2bee0db5e2c3f07155776dd6311805", size = 57702, upload-time = "2025-04-22T21:19:31.221Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/1e/2f/d20ad6e0113f982ea255fcb4ed94f70d0111757d7d03bfacebc2d9f60ba4/mypy_boto3_cloudformation-1.38.0-py3-none-any.whl", hash = "sha256:a1411aa5875b737492aaac5f7e8ce450f034c18f972eb608a9eba6fe35837f6a", size = 69607, upload-time = "2025-04-22T21:19:29.235Z" },
]
[[package]]
name = "mypy-boto3-dynamodb"
version = "1.38.4"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/6b/7f/72b68d275a80a42675c36249b80dd79ec5c7d9bd1f5cc93cdb572f866722/mypy_boto3_dynamodb-1.38.4.tar.gz", hash = "sha256:5cf3787631e312b3d75f89a6cbbbd4ad786a76f5d565af023febf03fbf23c0b5", size = 47461, upload-time = "2025-04-28T19:26:22.728Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/6a/35/3d0ceabb0a9f3765f509cb9dce6ddfa939114682b1acc442f52a755e9bc8/mypy_boto3_dynamodb-1.38.4-py3-none-any.whl", hash = "sha256:6b29d89c649eeb1e894118bee002cb8b1304c78da735b1503aa08e46b0abfdec", size = 56395, upload-time = "2025-04-28T19:26:16.947Z" },
]
[[package]]
name = "mypy-boto3-ec2"
version = "1.38.25"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d4/88/b9a99e010224b2ad9ea45b96f6689d9706841f2788e5ffe114cb0041e543/mypy_boto3_ec2-1.38.25.tar.gz", hash = "sha256:aed7d746c7c6af7e3f75424ad64829a7ce5b94dc871114a449c403ada22954cb", size = 400494, upload-time = "2025-05-28T19:42:21.197Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b4/22/f7caf22d014c7b0b62546a41b49030b73c162702870bfd1b1aaf48e2cc05/mypy_boto3_ec2-1.38.25-py3-none-any.whl", hash = "sha256:bad444d731669eab25fdcb7259901cb0db0fb26a4e1a79836a32aef6d674dbd0", size = 389882, upload-time = "2025-05-28T19:42:17.484Z" },
]
[[package]]
name = "mypy-boto3-lambda"
version = "1.38.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/8f/e3/0436071b28942788bdd22d6f91847654a7b1d167fb9d86c5779108e49ee9/mypy_boto3_lambda-1.38.0.tar.gz", hash = "sha256:ece7b3848c045e1be81c4f2b7482002c17ce7cb70de850661146103a8cb1a3fb", size = 41767, upload-time = "2025-04-22T21:27:54.666Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/17/09/602a39b39abd0d58d8b6bbee4c1552b64fadba2324676d7d45c3fa00fe7b/mypy_boto3_lambda-1.38.0-py3-none-any.whl", hash = "sha256:0dcb882826f61fd2751f6b98330b0e11085570654db85318aea018374ca88dc9", size = 48210, upload-time = "2025-04-22T21:27:52.034Z" },
]
[[package]]
name = "mypy-boto3-rds"
version = "1.38.20"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/97/b7/5ac46bee6617c8f955f6c28b8698a954f537812d84d655e3c887557421f0/mypy_boto3_rds-1.38.20.tar.gz", hash = "sha256:c6aa70c0cc5bc59959fec434206fbf8200386b583ff1f7e372154eaa41eb52e9", size = 85121, upload-time = "2025-05-20T23:30:09.631Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e7/ff/1a82134124ef41c040a40076c83b111d760f57007ec7b16649891fef0473/mypy_boto3_rds-1.38.20-py3-none-any.whl", hash = "sha256:9f600c24e687780fed1c8dc6d244b17dd0889f34705ec40c66df15e1caa420f4", size = 91368, upload-time = "2025-05-20T23:30:05.508Z" },
]
[[package]]
name = "mypy-boto3-s3"
version = "1.38.26"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/79/a5/366aec375b77cfe7820b7b3213318b147aefda6f12a035691541a5d557d1/mypy_boto3_s3-1.38.26.tar.gz", hash = "sha256:38a45dee5782d5c07ddea07ea50965c4d2ba7e77617c19f613b4c9f80f961b52", size = 73717, upload-time = "2025-05-29T19:43:03.468Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/3b/fa/251b651c18341c7491909994bd459b12ad05e13059d65bfa65d3afabdf8d/mypy_boto3_s3-1.38.26-py3-none-any.whl", hash = "sha256:1129d64be1aee863e04f0c92ac8d315578f13ccae64fa199b20ad0950d2b9616", size = 80321, upload-time = "2025-05-29T19:42:59.199Z" },
]
[[package]]
name = "mypy-boto3-sqs"
version = "1.38.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f0/a0/ef5c7bdb33af5d0a48029fed11401388fa68949c6c0f9b11b2e845f5fe0e/mypy_boto3_sqs-1.38.0.tar.gz", hash = "sha256:39aebc121a2fe20f962fd83b617fd916003605d6f6851fdf195337a0aa428fe1", size = 23541, upload-time = "2025-04-22T21:35:17.315Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/a5/97/72fccc9aaa0e3c8f3f99b4edac580ede651808aefb47b0d2b52c18a3d16b/mypy_boto3_sqs-1.38.0-py3-none-any.whl", hash = "sha256:8e881c8492f6f51dcbe1cce9d9f05334f4b256b5843e227fa925e0f6e702b31d", size = 33669, upload-time = "2025-04-22T21:35:16.073Z" },
]
[[package]]
name = "orjson"
version = "3.10.16"
@@ -938,6 +1039,24 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540, upload-time = "2024-11-24T20:12:19.698Z" },
]
[[package]]
name = "types-awscrt"
version = "0.27.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/36/6c/583522cfb3c330e92e726af517a91c13247e555e021791a60f1b03c6ff16/types_awscrt-0.27.2.tar.gz", hash = "sha256:acd04f57119eb15626ab0ba9157fc24672421de56e7bd7b9f61681fedee44e91", size = 16304, upload-time = "2025-05-16T03:10:08.712Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/4c/82/1ee2e5c9d28deac086ab3a6ff07c8bc393ef013a083f546c623699881715/types_awscrt-0.27.2-py3-none-any.whl", hash = "sha256:49a045f25bbd5ad2865f314512afced933aed35ddbafc252e2268efa8a787e4e", size = 37761, upload-time = "2025-05-16T03:10:07.466Z" },
]
[[package]]
name = "types-s3transfer"
version = "0.13.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/42/c1/45038f259d6741c252801044e184fec4dbaeff939a58f6160d7c32bf4975/types_s3transfer-0.13.0.tar.gz", hash = "sha256:203dadcb9865c2f68fb44bc0440e1dc05b79197ba4a641c0976c26c9af75ef52", size = 14175, upload-time = "2025-05-28T02:16:07.614Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c8/5d/6bbe4bf6a79fb727945291aef88b5ecbdba857a603f1bbcf1a6be0d3f442/types_s3transfer-0.13.0-py3-none-any.whl", hash = "sha256:79c8375cbf48a64bff7654c02df1ec4b20d74f8c5672fc13e382f593ca5565b3", size = 19588, upload-time = "2025-05-28T02:16:06.709Z" },
]
[[package]]
name = "typing-extensions"
version = "4.13.0"