From 896e0f1054df4e0f38c069fed49d47566f951938 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Rafael=20Siqueira?= Date: Tue, 22 Jul 2025 16:02:19 -0300 Subject: [PATCH] add delete --- README.md | 10 ++++--- http-api/app/routes/orgs/custom_pricing.py | 33 ++++++++++++++++++---- http-api/tests/routes/test_orgs.py | 27 ++++++++++++++++++ 3 files changed, 60 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index f72bda4..d3053b4 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,8 @@ O gestor responsável pela ação também é relacionado à compra, com base no {"id": "101", "sk": "SLOT", "status": "PENDING", "mode": "BATCH"} {"id": "101", "sk": "SLOT#ENROLLMENT#9omWNKymwU5U4aeun6mWzZ", "status": "SUCCESS"} {"id": "101", "sk": "SLOT#ENROLLMENT#12", "status": "ROLLBACK"} +{"id": "101", "sk": "ITEMS", "items": []} +{"id": "101", "sk": "NFSE", "nfse": "123"} ``` Quando o responsável é uma pessoa física (CPF). @@ -35,7 +37,7 @@ Quando o responsável é uma pessoa física (CPF). ```json {"id": "9omWNKymwU5U4aeun6mWzZ", "sk": "0", "course": {"id": "10", "name": "pytest"}, "org_id": "100"} {"id": "9omWNKymwU5U4aeun6mWzZ", "sk": "METADATA#COURSE", "access_period": 360, "cert": {"exp_interval": 365}} -{"id": "9omWNKymwU5U4aeun6mWzZ", "sk": "TENANT", "org_id": "100", "name": "EDUSEG"} +{"id": "9omWNKymwU5U4aeun6mWzZ", "sk": "OWNER_ORG", "org_id": "100", "name": "EDUSEG"} {"id": "9omWNKymwU5U4aeun6mWzZ", "sk": "AUTHOR", "user_id": "202", "name": "Tiago Maciel"} {"id": "9omWNKymwU5U4aeun6mWzZ", "sk": "KONVIVA", "user_id": 122, "class_id": 123, "enrollment_id": 1239} {"id": "9omWNKymwU5U4aeun6mWzZ", "sk": "STARTED", "started_at": "2025-04-06T11:07:32.762178-03:00"} @@ -93,14 +95,14 @@ Se um certificado for emitido para a matrícula, o período de proteção será ### Política de cancelamento -Apenas matrículas com `cancel_policy` podem ser canceladas. +Apenas matrículas com `CANCEL_POLICY` podem ser canceladas. -Se houver `metadata#parent_slot`, deve ser devolvido. +Se houver `METADATA#SOURCE_SLOT`, deve ser devolvido. ```json {"id": "9omWNKymwU5U4aeun6mWzZ", "sk": "0"} {"id": "9omWNKymwU5U4aeun6mWzZ", "sk": "CANCEL_POLICY"} -{"id": "9omWNKymwU5U4aeun6mWzZ", "sk": "METADATA#PARENT_SLOT", "slot": {"id": "SLOT#ORG#123", "sk": "ORDER#1221#ENROLLMENT#9omWNKymwU5U4aeun6mWzZ"}} +{"id": "9omWNKymwU5U4aeun6mWzZ", "sk": "METADATA#SOURCE_SLOT", "slot": {"id": "SLOT#ORG#123", "sk": "ORDER#1221#ENROLLMENT#9omWNKymwU5U4aeun6mWzZ"}} ``` # Cursos diff --git a/http-api/app/routes/orgs/custom_pricing.py b/http-api/app/routes/orgs/custom_pricing.py index c163b2a..a8c6904 100644 --- a/http-api/app/routes/orgs/custom_pricing.py +++ b/http-api/app/routes/orgs/custom_pricing.py @@ -22,11 +22,6 @@ router = Router() course_layer = DynamoDBPersistenceLayer(COURSE_TABLE, dynamodb_client) -class CustomPricing(BaseModel): - course_id: UUID4 - unit_price: Decimal - - @router.get('//custompricing', compress=True) def get_custom_pricing(id: str): result = course_layer.collection.query( @@ -37,6 +32,11 @@ def get_custom_pricing(id: str): return result +class CustomPricing(BaseModel): + course_id: UUID4 + unit_price: Decimal + + @router.post('//custompricing', compress=True) def post_custom_pricing(id: str, payload: CustomPricing): now_ = now() @@ -46,8 +46,11 @@ def post_custom_pricing(id: str, payload: CustomPricing): item={ 'id': f'CUSTOM_PRICING#ORG#{id}', 'sk': f'COURSE#{payload.course_id}', + 'unit_price': payload.unit_price, 'created_at': now_, - } + }, + cond_expr='attribute_not_exists(sk)', + exc_cls=CoursConflictError, ) transact.condition( key=KeyPair(str(payload.course_id), '0'), @@ -58,4 +61,22 @@ def post_custom_pricing(id: str, payload: CustomPricing): return JSONResponse(status_code=HTTPStatus.CREATED) +class Delete(BaseModel): + course_id: UUID4 + + +@router.delete('//custompricing', compress=True) +def delete_custom_pricing(id: str, payload: Delete): + if course_layer.delete_item( + KeyPair( + f'CUSTOM_PRICING#ORG#{id}', + f'COURSE#{payload.course_id}', + ) + ): + return JSONResponse(status_code=HTTPStatus.OK) + + +class CoursConflictError(BadRequestError): ... + + class CourseNotFoundError(BadRequestError): ... diff --git a/http-api/tests/routes/test_orgs.py b/http-api/tests/routes/test_orgs.py index 5888630..d6ba8ff 100644 --- a/http-api/tests/routes/test_orgs.py +++ b/http-api/tests/routes/test_orgs.py @@ -142,6 +142,33 @@ def test_get_custom_pricing( assert json.loads(r['body']) == expected +def test_delete_custom_pricing( + mock_app, + dynamodb_seeds, + dynamodb_persistence_layer: DynamoDBPersistenceLayer, + http_api_proxy: HttpApiProxy, + lambda_context: LambdaContext, +): + mock_app.lambda_handler( + http_api_proxy( + raw_path='/orgs/cJtK9SsnJhKPyxESe7g3DG/custompricing', + method=HTTPMethod.DELETE, + body={'course_id': '281198c2-f293-4acc-b96e-e4a2d5f6b73c'}, + ), + lambda_context, + ) + + data = dynamodb_persistence_layer.collection.get_item( + KeyPair( + 'CUSTOM_PRICING#ORG#cJtK9SsnJhKPyxESe7g3DG', + 'COURSE#15ee05a3-4ceb-4b7e-9979-db75b28c9ade', + ), + raise_on_error=False, + ) + + assert not data + + def test_post_custom_pricing( mock_app, dynamodb_seeds,