update id
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -11,3 +11,4 @@ requirements.txt
|
|||||||
.dynamodb_volume/
|
.dynamodb_volume/
|
||||||
.elastic_volume/
|
.elastic_volume/
|
||||||
.env
|
.env
|
||||||
|
node_modules/
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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'
|
||||||
|
|||||||
@@ -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,
|
|
||||||
)
|
)
|
||||||
|
|||||||
4
api.saladeaula.digital/app/routes/orgs/__init__.py
Normal file
4
api.saladeaula.digital/app/routes/orgs/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
|
||||||
|
from .custom_pricing import router as custom_pricing
|
||||||
|
|
||||||
|
__all__ = ['custom_pricing']
|
||||||
17
api.saladeaula.digital/app/routes/orgs/custom_pricing.py
Normal file
17
api.saladeaula.digital/app/routes/orgs/custom_pricing.py
Normal 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,
|
||||||
|
)
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
24
api.saladeaula.digital/tests/routes/test_enrollments.py
Normal file
24
api.saladeaula.digital/tests/routes/test_enrollments.py
Normal 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
|
||||||
@@ -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"}}
|
||||||
2
api.saladeaula.digital/uv.lock
generated
2
api.saladeaula.digital/uv.lock
generated
@@ -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" },
|
||||||
|
|||||||
@@ -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
2
http-api/uv.lock
generated
@@ -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" },
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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']
|
||||||
|
|||||||
@@ -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 = [
|
||||||
|
|||||||
Reference in New Issue
Block a user