fix block

add check name to subscription
This commit is contained in:
2026-01-19 11:59:52 -03:00
parent 2f390ab202
commit 6b472110e2
6 changed files with 531 additions and 386 deletions

View File

@@ -11,6 +11,20 @@ class ConflictError(ServiceError):
super().__init__(HTTPStatus.CONFLICT, msg) super().__init__(HTTPStatus.CONFLICT, msg)
class NotAcceptableError(ServiceError):
def __init__(self, msg: str | dict):
super().__init__(HTTPStatus.NOT_ACCEPTABLE, msg)
class SubscriptionRequiredError(NotAcceptableError): ...
class SubscriptionFrozenError(NotAcceptableError): ...
class SubscriptionConflictError(ConflictError): ...
class OrgNotFoundError(NotFoundError): ... class OrgNotFoundError(NotFoundError): ...

View File

@@ -1,14 +1,10 @@
from datetime import date, datetime, time, timedelta from datetime import date, datetime, time, timedelta
from http import HTTPStatus
from typing import Annotated, TypedDict from typing import Annotated, TypedDict
from uuid import uuid4 from uuid import uuid4
import pytz import pytz
from aws_lambda_powertools import Logger from aws_lambda_powertools import Logger
from aws_lambda_powertools.event_handler.api_gateway import Router from aws_lambda_powertools.event_handler.api_gateway import Router
from aws_lambda_powertools.event_handler.exceptions import (
ServiceError,
)
from aws_lambda_powertools.event_handler.openapi.params import Body from aws_lambda_powertools.event_handler.openapi.params import Body
from aws_lambda_powertools.shared.functions import extract_event_from_common_models from aws_lambda_powertools.shared.functions import extract_event_from_common_models
from layercake.batch import BatchProcessor from layercake.batch import BatchProcessor
@@ -20,7 +16,7 @@ from pydantic import UUID4, BaseModel, EmailStr, Field, FutureDate
from boto3clients import dynamodb_client from boto3clients import dynamodb_client
from config import DEDUP_WINDOW_OFFSET_DAYS, ENROLLMENT_TABLE, TZ, USER_TABLE from config import DEDUP_WINDOW_OFFSET_DAYS, ENROLLMENT_TABLE, TZ, USER_TABLE
from exceptions import ConflictError from exceptions import ConflictError, SubscriptionFrozenError, SubscriptionRequiredError
from middlewares.authentication_middleware import User as Authenticated from middlewares.authentication_middleware import User as Authenticated
logger = Logger(__name__) logger = Logger(__name__)
@@ -29,16 +25,6 @@ dyn = DynamoDBPersistenceLayer(ENROLLMENT_TABLE, dynamodb_client)
processor = BatchProcessor() processor = BatchProcessor()
class SubscriptionRequiredError(ServiceError):
def __init__(self, msg: str | dict):
super().__init__(HTTPStatus.NOT_ACCEPTABLE, msg)
class SubscriptionFrozenError(ServiceError):
def __init__(self, msg: str | dict):
super().__init__(HTTPStatus.NOT_ACCEPTABLE, msg)
class DeduplicationConflictError(ConflictError): ... class DeduplicationConflictError(ConflictError): ...
@@ -139,7 +125,7 @@ def enroll(
dyn.put_item(item=item) dyn.put_item(item=item)
except Exception as exc: except Exception as exc:
logger.exception(exc) logger.exception(exc)
finally: else:
return item return item

View File

@@ -10,14 +10,17 @@ from layercake.dynamodb import DynamoDBPersistenceLayer, KeyPair
from api_gateway import JSONResponse from api_gateway import JSONResponse
from boto3clients import dynamodb_client from boto3clients import dynamodb_client
from config import USER_TABLE from config import USER_TABLE
from exceptions import OrgNotFoundError from exceptions import (
OrgNotFoundError,
SubscriptionConflictError,
SubscriptionRequiredError,
)
router = Router() router = Router()
dyn = DynamoDBPersistenceLayer(USER_TABLE, dynamodb_client) dyn = DynamoDBPersistenceLayer(USER_TABLE, dynamodb_client)
class PaymentMethod(str, Enum): class PaymentMethod(str, Enum):
PIX = 'PIX'
BANK_SLIP = 'BANK_SLIP' BANK_SLIP = 'BANK_SLIP'
MANUAL = 'MANUAL' MANUAL = 'MANUAL'
@@ -26,7 +29,7 @@ class PaymentMethod(str, Enum):
def add( def add(
org_id: str, org_id: str,
name: Annotated[str, Body(embed=True)], name: Annotated[str, Body(embed=True)],
billing_day: Annotated[int, Body(embed=True, ge=1)], billing_day: Annotated[int, Body(embed=True, ge=1, le=31)],
payment_method: Annotated[PaymentMethod, Body(embed=True)], payment_method: Annotated[PaymentMethod, Body(embed=True)],
): ):
now_ = now() now_ = now()
@@ -35,11 +38,15 @@ def add(
transact.update( transact.update(
key=KeyPair(org_id, '0'), key=KeyPair(org_id, '0'),
update_expr='SET subscription_covered = :true, updated_at = :now', update_expr='SET subscription_covered = :true, updated_at = :now',
cond_expr='attribute_exists(sk) AND #name = :name',
expr_attr_names={
'#name': 'name',
},
expr_attr_values={ expr_attr_values={
':name': name,
':true': True, ':true': True,
':now': now_, ':now': now_,
}, },
cond_expr='attribute_exists(sk)',
exc_cls=OrgNotFoundError, exc_cls=OrgNotFoundError,
) )
transact.put( transact.put(
@@ -49,7 +56,9 @@ def add(
'billing_day': billing_day, 'billing_day': billing_day,
'payment_method': payment_method.value, 'payment_method': payment_method.value,
'created_at': now_, 'created_at': now_,
} },
cond_expr='attribute_not_exists(sk)',
exc_cls=SubscriptionConflictError,
) )
transact.put( transact.put(
item={ item={
@@ -59,6 +68,71 @@ def add(
'created_at': now_, 'created_at': now_,
}, },
cond_expr='attribute_not_exists(sk)', cond_expr='attribute_not_exists(sk)',
exc_cls=SubscriptionConflictError,
) )
return JSONResponse(status_code=HTTPStatus.CREATED) return JSONResponse(status_code=HTTPStatus.CREATED)
@router.put('/<org_id>/subscription')
def edit(
org_id: str,
billing_day: Annotated[int, Body(embed=True, ge=1, le=31)],
payment_method: Annotated[PaymentMethod, Body(embed=True)],
subscription_frozen: Annotated[bool, Body(embed=True)] = False,
):
now_ = now()
with dyn.transact_writer() as transact:
transact.condition(
key=KeyPair('SUBSCRIPTION', f'ORG#{org_id}'),
cond_expr='attribute_exists(sk)',
exc_cls=SubscriptionRequiredError,
)
transact.update(
key=KeyPair(org_id, 'METADATA#SUBSCRIPTION'),
update_expr='SET billing_day = :billing_day, \
payment_method = :payment_method, \
updated_at = :now',
expr_attr_values={
':billing_day': billing_day,
':payment_method': payment_method.value,
':now': now_,
},
cond_expr='attribute_exists(sk)',
exc_cls=OrgNotFoundError,
)
if subscription_frozen:
transact.put(
item={
'id': 'SUBSCRIPTION#FREEZE',
'sk': f'ORG#{org_id}',
'created_at': now_,
}
)
else:
transact.delete(key=KeyPair('SUBSCRIPTION#FREEZE', f'ORG#{org_id}'))
return JSONResponse(status_code=HTTPStatus.NO_CONTENT)
@router.delete('/<org_id>/subscription')
def remove(org_id: str):
now_ = now()
with dyn.transact_writer() as transact:
transact.update(
key=KeyPair(org_id, '0'),
update_expr='SET updated_at = :now REMOVE subscription_covered',
expr_attr_values={
':now': now_,
},
cond_expr='attribute_exists(sk)',
exc_cls=OrgNotFoundError,
)
transact.delete(key=KeyPair(org_id, 'METADATA#SUBSCRIPTION'))
transact.delete(key=KeyPair('SUBSCRIPTION', f'ORG#{org_id}'))
transact.delete(key=KeyPair('SUBSCRIPTION#FREEZE', f'ORG#{org_id}'))
return JSONResponse(status_code=HTTPStatus.NO_CONTENT)

View File

@@ -17,7 +17,7 @@ def test_subscription(
): ):
r = app.lambda_handler( r = app.lambda_handler(
http_api_proxy( http_api_proxy(
raw_path='/orgs/2a8963fc-4694-4fe2-953a-316d1b10f1f5/subscription', raw_path='/orgs/2a8963fc-4694-4fe2-953a-316d1b10f1f5',
method=HTTPMethod.GET, method=HTTPMethod.GET,
), ),
lambda_context, lambda_context,

View File

@@ -38,7 +38,7 @@
{"id": "cnpj", "sk": "00000000000191", "org_id": "6000f79-6e5c-49a0-952f-3bda330ef278"} {"id": "cnpj", "sk": "00000000000191", "org_id": "6000f79-6e5c-49a0-952f-3bda330ef278"}
{"id": "SUBSCRIPTION", "sk": "ORG#2a8963fc-4694-4fe2-953a-316d1b10f1f5"} {"id": "SUBSCRIPTION", "sk": "ORG#2a8963fc-4694-4fe2-953a-316d1b10f1f5"}
{"id": "SUBSCRIPTION", "sk": "ORG#cJtK9SsnJhKPyxESe7g3DG"} {"id": "SUBSCRIPTION", "sk": "ORG#cJtK9SsnJhKPyxESe7g3DG"}
{"id": "SUBSCRIPTION#FROZEN", "sk": "ORG#2a8963fc-4694-4fe2-953a-316d1b10f1f5", "frozen": true, "created_at": "2025-12-24T00:05:27-03:00"} {"id": "SUBSCRIPTION#FREEZE", "sk": "ORG#2a8963fc-4694-4fe2-953a-316d1b10f1f5", "created_at": "2025-12-24T00:05:27-03:00"}
// CPFs // CPFs
{"id": "cpf", "sk": "07879819908", "user_id": "15bacf02-1535-4bee-9022-19d106fd7518"} {"id": "cpf", "sk": "07879819908", "user_id": "15bacf02-1535-4bee-9022-19d106fd7518"}

File diff suppressed because it is too large Load Diff