add subscription test

This commit is contained in:
2026-01-16 19:47:41 -03:00
parent f593b21a73
commit 753c4be4da
8 changed files with 142 additions and 23 deletions

View File

@@ -57,6 +57,7 @@ app.include_router(orgs.billing, prefix='/orgs')
app.include_router(orgs.custom_pricing, prefix='/orgs') app.include_router(orgs.custom_pricing, prefix='/orgs')
app.include_router(orgs.scheduled, prefix='/orgs') app.include_router(orgs.scheduled, prefix='/orgs')
app.include_router(orgs.submissions, prefix='/orgs') app.include_router(orgs.submissions, prefix='/orgs')
app.include_router(orgs.subscription, prefix='/orgs')
app.include_router(orgs.seats, prefix='/orgs') app.include_router(orgs.seats, prefix='/orgs')
app.include_router(orgs.users, prefix='/orgs') app.include_router(orgs.users, prefix='/orgs')
app.include_router(orgs.batch_jobs, prefix='/orgs') app.include_router(orgs.batch_jobs, prefix='/orgs')

View File

@@ -11,6 +11,12 @@ class ConflictError(ServiceError):
super().__init__(HTTPStatus.CONFLICT, msg) super().__init__(HTTPStatus.CONFLICT, msg)
class OrgNotFoundError(NotFoundError): ...
class MemberNotFoundError(NotFoundError): ...
class OrderNotFoundError(NotFoundError): ... class OrderNotFoundError(NotFoundError): ...

View File

@@ -6,6 +6,7 @@ from .custom_pricing import router as custom_pricing
from .enrollments.scheduled import router as scheduled from .enrollments.scheduled import router as scheduled
from .enrollments.submissions import router as submissions from .enrollments.submissions import router as submissions
from .seats import router as seats from .seats import router as seats
from .subscription import router as subscription
from .users.add import router as users from .users.add import router as users
from .users.batch_jobs import router as batch_jobs from .users.batch_jobs import router as batch_jobs
@@ -18,6 +19,7 @@ __all__ = [
'scheduled', 'scheduled',
'submissions', 'submissions',
'seats', 'seats',
'subscription',
'users', 'users',
'batch_jobs', 'batch_jobs',
] ]

View File

@@ -2,7 +2,6 @@ from http import HTTPStatus
from typing import Annotated from typing import Annotated
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 NotFoundError
from aws_lambda_powertools.event_handler.openapi.params import Body from aws_lambda_powertools.event_handler.openapi.params import Body
from layercake.dateutils import now from layercake.dateutils import now
from layercake.dynamodb import DynamoDBPersistenceLayer, KeyPair from layercake.dynamodb import DynamoDBPersistenceLayer, KeyPair
@@ -12,17 +11,12 @@ from pydantic import UUID4, BaseModel, EmailStr
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 MemberNotFoundError, OrgNotFoundError
router = Router() router = Router()
dyn = DynamoDBPersistenceLayer(USER_TABLE, dynamodb_client) dyn = DynamoDBPersistenceLayer(USER_TABLE, dynamodb_client)
class OrgNotFoundError(NotFoundError): ...
class MemberNotFoundError(NotFoundError): ...
@router.get('/<org_id>/admins') @router.get('/<org_id>/admins')
def admins(org_id: str): def admins(org_id: str):
return dyn.collection.query( return dyn.collection.query(

View File

@@ -7,7 +7,7 @@ from aws_lambda_powertools.event_handler.openapi.params import Query
from layercake.dynamodb import DynamoDBPersistenceLayer, KeyPair from layercake.dynamodb import DynamoDBPersistenceLayer, KeyPair
from boto3clients import dynamodb_client from boto3clients import dynamodb_client
from config import ORDER_TABLE, USER_TABLE from config import ORDER_TABLE
router = Router() router = Router()
dyn = DynamoDBPersistenceLayer(ORDER_TABLE, dynamodb_client) dyn = DynamoDBPersistenceLayer(ORDER_TABLE, dynamodb_client)
@@ -16,19 +16,6 @@ dyn = DynamoDBPersistenceLayer(ORDER_TABLE, dynamodb_client)
class BillingNotFoundError(NotFoundError): ... class BillingNotFoundError(NotFoundError): ...
@router.get('/<org_id>/subscription')
def subscription(org_id: str):
return dyn.collection.get_item(
KeyPair(
pk=org_id,
sk='METADATA#SUBSCRIPTION',
table_name=USER_TABLE,
),
raise_on_error=False,
default={},
)
@router.get('/<org_id>/billing') @router.get('/<org_id>/billing')
def billing( def billing(
org_id: str, org_id: str,

View File

@@ -0,0 +1,71 @@
from enum import Enum
from http import HTTPStatus
from typing import Annotated
from aws_lambda_powertools.event_handler.api_gateway import Router
from aws_lambda_powertools.event_handler.openapi.params import Body
from layercake.dateutils import now
from layercake.dynamodb import DynamoDBPersistenceLayer, KeyPair
from api_gateway import JSONResponse
from boto3clients import dynamodb_client
from config import USER_TABLE
from exceptions import OrgNotFoundError
router = Router()
dyn = DynamoDBPersistenceLayer(USER_TABLE, dynamodb_client)
class PaymentMethod(str, Enum):
PIX = 'PIX'
BANK_SLIP = 'BANK_SLIP'
MANUAL = 'MANUAL'
@router.get('/<org_id>/subscription')
def subscription(org_id: str):
return dyn.collection.get_item(
KeyPair(
pk=org_id,
sk='METADATA#SUBSCRIPTION',
),
raise_on_error=False,
default={},
)
@router.post('/<org_id>/subscription')
def add(
org_id: str,
name: Annotated[str, Body(embed=True)],
billing_day: Annotated[int, Body(embed=True, ge=1)],
payment_method: Annotated[PaymentMethod, Body(embed=True)],
):
now_ = now()
with dyn.transact_writer() as transact:
transact.condition(
key=KeyPair(org_id, '0'),
cond_expr='attribute_exists(sk)',
exc_cls=OrgNotFoundError,
)
transact.put(
item={
'id': 'SUBSCRIPTION',
'sk': f'ORG#{org_id}',
'name': name,
'created_at': now_,
},
cond_expr='attribute_not_exists(sk)',
)
transact.put(
item={
'id': org_id,
'sk': 'METADATA#SUBSCRIPTION',
'billing_day': billing_day,
'payment_method': payment_method.value,
'created_at': now_,
}
)
return JSONResponse(status_code=HTTPStatus.CREATED)

View File

@@ -3,7 +3,6 @@ from typing import Annotated
from uuid import uuid4 from uuid import uuid4
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 NotFoundError
from aws_lambda_powertools.event_handler.openapi.params import Body from aws_lambda_powertools.event_handler.openapi.params import Body
from layercake.dateutils import now, ttl from layercake.dateutils import now, ttl
from layercake.dynamodb import DynamoDBPersistenceLayer, KeyPair, SortKey from layercake.dynamodb import DynamoDBPersistenceLayer, KeyPair, SortKey
@@ -16,6 +15,7 @@ from config import INTERNAL_EMAIL_DOMAIN, USER_TABLE
from exceptions import ( from exceptions import (
CPFConflictError, CPFConflictError,
EmailConflictError, EmailConflictError,
OrgNotFoundError,
UserConflictError, UserConflictError,
UserNotFoundError, UserNotFoundError,
) )
@@ -37,7 +37,7 @@ class User(BaseModel):
email: EmailStr email: EmailStr
class OrgNotFoundError(NotFoundError): ... # class OrgNotFoundError(NotFoundError): ...
@router.post('/<org_id>/users') @router.post('/<org_id>/users')

View File

@@ -0,0 +1,58 @@
from http import HTTPMethod, HTTPStatus
from layercake.dynamodb import (
DynamoDBPersistenceLayer,
KeyPair,
)
from ...conftest import HttpApiProxy, LambdaContext
def test_subscription(
app,
seeds,
http_api_proxy: HttpApiProxy,
dynamodb_persistence_layer: DynamoDBPersistenceLayer,
lambda_context: LambdaContext,
):
r = app.lambda_handler(
http_api_proxy(
raw_path='/orgs/2a8963fc-4694-4fe2-953a-316d1b10f1f5/subscription',
method=HTTPMethod.GET,
),
lambda_context,
)
assert r['statusCode'] == HTTPStatus.OK
def test_add_subscription(
app,
seeds,
http_api_proxy: HttpApiProxy,
dynamodb_persistence_layer: DynamoDBPersistenceLayer,
lambda_context: LambdaContext,
):
org_id = 'f6000f79-6e5c-49a0-952f-3bda330ef278'
r = app.lambda_handler(
http_api_proxy(
raw_path=f'/orgs/{org_id}/subscription',
method=HTTPMethod.POST,
body={
'name': 'Banco do Brasil',
'billing_day': 1,
'payment_method': 'MANUAL',
},
),
lambda_context,
)
assert r['statusCode'] == HTTPStatus.CREATED
r = dynamodb_persistence_layer.collection.get_items(
KeyPair(org_id, '0')
+ KeyPair(org_id, 'METADATA#SUBSCRIPTION', rename_key='metadata')
+ KeyPair('SUBSCRIPTION', f'ORG#{org_id}', rename_key='subscription')
)
assert r['metadata']['billing_day'] == 1
assert r['metadata']['payment_method'] == 'MANUAL'
assert r['subscription']['name'] == 'Banco do Brasil'