update id

This commit is contained in:
2025-10-30 02:02:23 -03:00
parent f284b64c60
commit 76dfc44b71
25 changed files with 103 additions and 40 deletions

1
.gitignore vendored
View File

@@ -11,3 +11,4 @@ requirements.txt
.dynamodb_volume/ .dynamodb_volume/
.elastic_volume/ .elastic_volume/
.env .env
node_modules/

View File

@@ -14,7 +14,7 @@ from aws_lambda_powertools.utilities.typing import LambdaContext
from api_gateway import JSONResponse from api_gateway import JSONResponse
from json_encoder import JSONEncoder from json_encoder import JSONEncoder
from routes import courses, enrollments, orders, users from routes import courses, enrollments, orders, orgs, users
logger = Logger(__name__) logger = Logger(__name__)
tracer = Tracer() tracer = Tracer()
@@ -42,6 +42,7 @@ app.include_router(users.router, prefix='/users')
app.include_router(users.emails, prefix='/users') app.include_router(users.emails, prefix='/users')
app.include_router(users.orgs, prefix='/users') app.include_router(users.orgs, prefix='/users')
app.include_router(orders.router, prefix='/orders') app.include_router(orders.router, prefix='/orders')
app.include_router(orgs.custom_pricing, prefix='/orgs')
@app.exception_handler(ServiceError) @app.exception_handler(ServiceError)

View File

@@ -51,17 +51,18 @@ class Course(BaseModel):
@router.put('/<course_id>') @router.put('/<course_id>')
def edit_course(course_id: str): def put_course(course_id: str):
event = router.current_event event = router.current_event
if not event.decoded_body: if not event.decoded_body:
raise BadRequestError('Invalid request body') raise BadRequestError('Invalid request body')
body = BytesIO(event.decoded_body.encode())
course = Course.model_validate(
{'id': course_id, 'cert': {}} | parse(event.headers, body),
)
now_ = now() now_ = now()
body = parse(
event.headers,
BytesIO(event.decoded_body.encode()),
)
course = Course.model_validate({'id': course_id, 'cert': {}} | body)
if course.rawfile: if course.rawfile:
object_key = f'certs/templates/{course_id}.html' object_key = f'certs/templates/{course_id}.html'

View File

@@ -1,7 +1,6 @@
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 NotFoundError from layercake.dynamodb import DynamoDBPersistenceLayer, SortKey, TransactKey
from layercake.dynamodb import DynamoDBPersistenceLayer, KeyPair
from boto3clients import dynamodb_client from boto3clients import dynamodb_client
from config import ENROLLMENT_TABLE from config import ENROLLMENT_TABLE
@@ -20,7 +19,6 @@ dyn = DynamoDBPersistenceLayer(ENROLLMENT_TABLE, dynamodb_client)
@router.get('/<enrollment_id>') @router.get('/<enrollment_id>')
def get_enrollment(enrollment_id: str): def get_enrollment(enrollment_id: str):
return dyn.collection.get_item( return dyn.collection.get_items(
KeyPair(enrollment_id, '0'), TransactKey(enrollment_id) + SortKey('0') + SortKey('ORG') + SortKey('LOCK'),
exc_cls=NotFoundError,
) )

View File

@@ -0,0 +1,4 @@
from .custom_pricing import router as custom_pricing
__all__ = ['custom_pricing']

View File

@@ -0,0 +1,17 @@
from aws_lambda_powertools.event_handler.api_gateway import Router
from layercake.dynamodb import DynamoDBPersistenceLayer, PartitionKey
from boto3clients import dynamodb_client
from config import COURSE_TABLE, USER_TABLE
router = Router()
dyn = DynamoDBPersistenceLayer(USER_TABLE, dynamodb_client)
@router.get('/<org_id>/custompricing')
def get_custom_pricing(org_id: str):
return dyn.collection.query(
PartitionKey(f'CUSTOM_PRICING#ORG#{org_id}'),
table_name=COURSE_TABLE,
limit=100,
)

View File

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

View File

@@ -18,6 +18,7 @@ def pytest_configure():
os.environ['TZ'] = 'America/Sao_Paulo' os.environ['TZ'] = 'America/Sao_Paulo'
os.environ['COURSE_TABLE'] = PYTEST_TABLE_NAME os.environ['COURSE_TABLE'] = PYTEST_TABLE_NAME
os.environ['USER_TABLE'] = PYTEST_TABLE_NAME os.environ['USER_TABLE'] = PYTEST_TABLE_NAME
os.environ['ENROLLMENT_TABLE'] = PYTEST_TABLE_NAME
os.environ['BUCKET_NAME'] = 'saladeaula.digital' os.environ['BUCKET_NAME'] = 'saladeaula.digital'
os.environ['DYNAMODB_PARTITION_KEY'] = PK os.environ['DYNAMODB_PARTITION_KEY'] = PK
os.environ['DYNAMODB_SORT_KEY'] = SK os.environ['DYNAMODB_SORT_KEY'] = SK

View File

@@ -0,0 +1,24 @@
import json
from http import HTTPMethod, HTTPStatus
from ..conftest import HttpApiProxy, LambdaContext
def test_get_enrollment(
app,
seeds,
http_api_proxy: HttpApiProxy,
lambda_context: LambdaContext,
):
r = app.lambda_handler(
http_api_proxy(
raw_path='/enrollments/578ec87f-94c7-4840-8780-bb4839cc7e64',
method=HTTPMethod.GET,
),
lambda_context,
)
assert r['statusCode'] == HTTPStatus.OK
body = json.loads(r['body'])
assert 'user' in body
assert 'course' in body

View File

@@ -5,3 +5,6 @@
// User orgs // User orgs
{"id": "213a6682-2c59-4404-9189-12eec0a846d4", "sk": "orgs#286f7729-7765-482a-880a-0b153ea799be", "name": "ACME", "cnpj": "00000000000191"} {"id": "213a6682-2c59-4404-9189-12eec0a846d4", "sk": "orgs#286f7729-7765-482a-880a-0b153ea799be", "name": "ACME", "cnpj": "00000000000191"}
// Enrollment
{"id": "578ec87f-94c7-4840-8780-bb4839cc7e64", "sk": "0", "course": {"id": "af3258f0-bccf-4781-aec6-d4c618d234a7", "name": "pytest", "access_period": 180}, "user": {"id": "068b4600-cc36-4b55-b832-bb620021705a", "name": "Benjamin Burnley", "email": "burnley@breakingbenjamin.com"}}

View File

@@ -592,7 +592,7 @@ wheels = [
[[package]] [[package]]
name = "layercake" name = "layercake"
version = "0.11.0" version = "0.11.1"
source = { directory = "../layercake" } source = { directory = "../layercake" }
dependencies = [ dependencies = [
{ name = "arnparse" }, { name = "arnparse" },

View File

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

2
http-api/uv.lock generated
View File

@@ -523,7 +523,7 @@ wheels = [
[[package]] [[package]]
name = "layercake" name = "layercake"
version = "0.11.0" version = "0.11.1"
source = { directory = "../layercake" } source = { directory = "../layercake" }
dependencies = [ dependencies = [
{ name = "arnparse" }, { name = "arnparse" },

View File

@@ -76,6 +76,7 @@ class OpenIDCode(OpenIDCode_):
).filter(scope) ).filter(scope)
if user.scope: if user.scope:
# Used to define permission granularity
user_info['scope'] = user.scope user_info['scope'] = user.scope
return user_info return user_info
@@ -247,10 +248,13 @@ class RefreshTokenGrant(grants.RefreshTokenGrant):
"""The authorization server MAY revoke the old refresh token after """The authorization server MAY revoke the old refresh token after
issuing a new refresh token to the client.""" issuing a new refresh token to the client."""
logger.debug('Revoking old refresh token', refresh_token=refresh_token)
token = getattr(refresh_token, 'refresh_token', None) token = getattr(refresh_token, 'refresh_token', None)
logger.debug('Revoking old refresh token', refresh_token=token)
user = refresh_token.get_user() user = refresh_token.get_user()
if not token:
return None
with dyn.transact_writer() as transact: with dyn.transact_writer() as transact:
transact.delete( transact.delete(
key=KeyPair( key=KeyPair(

View File

@@ -48,11 +48,13 @@ def authorize():
if not client_scopes.issubset(user_scopes): if not client_scopes.issubset(user_scopes):
raise ForbiddenError('Access denied') raise ForbiddenError('Access denied')
return server.create_authorization_response( response = server.create_authorization_response(
request=router.current_event, request=router.current_event,
grant_user=sub, grant_user=sub,
grant=grant, grant=grant,
) )
logger.debug(response)
except JoseError as err: except JoseError as err:
logger.exception(err) logger.exception(err)
raise BadRequestError(str(err)) raise BadRequestError(str(err))
@@ -62,6 +64,8 @@ def authorize():
status_code=err.status_code, status_code=err.status_code,
msg=dict(err.get_body()), # type: ignore msg=dict(err.get_body()), # type: ignore
) )
else:
return response
def _user_scopes(sub: str) -> set: def _user_scopes(sub: str) -> set:

View File

@@ -1,13 +1,17 @@
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 oauth2 import RevocationEndpoint, server from oauth2 import RevocationEndpoint, server
logger = Logger(__name__)
router = Router() router = Router()
@router.post('/revoke') @router.post('/revoke')
def revoke(): def revoke():
return server.create_endpoint_response( response = server.create_endpoint_response(
RevocationEndpoint.ENDPOINT_NAME, RevocationEndpoint.ENDPOINT_NAME,
router.current_event, router.current_event,
) )
logger.debug(response)
return response

View File

@@ -1,10 +1,14 @@
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 oauth2 import server from oauth2 import server
logger = Logger(__name__)
router = Router() router = Router()
@router.post('/token') @router.post('/token')
def issue_token(): def issue_token():
return server.create_token_response(router.current_event) response = server.create_token_response(router.current_event)
logger.debug(response)
return response

View File

@@ -59,7 +59,8 @@ export async function loader({ request, context }: Route.LoaderArgs) {
Location: loginUrl.toString() Location: loginUrl.toString()
} }
}) })
} catch { } catch (error) {
console.error(error)
return new Response(null, { status: httpStatus.INTERNAL_SERVER }) return new Response(null, { status: httpStatus.INTERNAL_SERVER })
} }
} }

View File

@@ -77,7 +77,8 @@ export async function action({ request, context }: Route.ActionArgs) {
status: httpStatus.FOUND, status: httpStatus.FOUND,
headers headers
}) })
} catch { } catch (error) {
console.error(error)
return Response.json({}, { status: httpStatus.INTERNAL_SERVER }) return Response.json({}, { status: httpStatus.INTERNAL_SERVER })
} }
} }

View File

@@ -1,21 +1,12 @@
import type { Route } from './+types' import type { Route } from './+types'
export async function action({ request, context }: Route.ActionArgs) { export const loader = proxy
const url = new URL(request.url) export const action = proxy
const issuerUrl = new URL(url.pathname, context.cloudflare.env.ISSUER_URL)
const r = await fetch(issuerUrl.toString(), {
method: request.method,
headers: request.headers,
body: await request.text()
})
return new Response(await r.text(), { async function proxy({
status: r.status, request,
headers: r.headers context
}) }: Route.ActionArgs): Promise<Response> {
}
export async function loader({ request, context }: Route.ActionArgs) {
const url = new URL(request.url) const url = new URL(request.url)
const issuerUrl = new URL(url.pathname, context.cloudflare.env.ISSUER_URL) const issuerUrl = new URL(url.pathname, context.cloudflare.env.ISSUER_URL)
const r = await fetch(issuerUrl.toString(), { const r = await fetch(issuerUrl.toString(), {
@@ -23,6 +14,8 @@ export async function loader({ request, context }: Route.ActionArgs) {
headers: request.headers headers: request.headers
}) })
console.log('[response]', r)
return new Response(await r.text(), { return new Response(await r.text(), {
status: r.status, status: r.status,
headers: r.headers headers: r.headers

View File

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

View File

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

View File

@@ -1124,6 +1124,7 @@ class DynamoDBCollection:
filter_expr: str | None = None, filter_expr: str | None = None,
index_forward: bool = False, index_forward: bool = False,
limit: int = LIMIT, limit: int = LIMIT,
table_name: str | None = None,
) -> PaginatedResult: ) -> PaginatedResult:
"""Query returns all items with that partition key or key pair. """Query returns all items with that partition key or key pair.
@@ -1185,6 +1186,7 @@ class DynamoDBCollection:
index_forward=index_forward, index_forward=index_forward,
limit=limit, limit=limit,
start_key=_startkey_b64decode(start_key) if start_key else {}, start_key=_startkey_b64decode(start_key) if start_key else {},
table_name=table_name,
) )
items = r['items'] items = r['items']

View File

@@ -1,6 +1,6 @@
[project] [project]
name = "layercake" name = "layercake"
version = "0.11.0" version = "0.11.1"
description = "Packages shared dependencies to optimize deployment and ensure consistency across functions." description = "Packages shared dependencies to optimize deployment and ensure consistency across functions."
readme = "README.md" readme = "README.md"
authors = [ authors = [