update url
This commit is contained in:
@@ -1,4 +1,6 @@
|
|||||||
|
from http import HTTPStatus
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
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
|
||||||
@@ -6,12 +8,14 @@ from aws_lambda_powertools.event_handler.exceptions import (
|
|||||||
BadRequestError,
|
BadRequestError,
|
||||||
NotFoundError,
|
NotFoundError,
|
||||||
)
|
)
|
||||||
|
from layercake.dateutils import now
|
||||||
from layercake.dynamodb import DynamoDBPersistenceLayer, KeyPair
|
from layercake.dynamodb import DynamoDBPersistenceLayer, KeyPair
|
||||||
from pydantic import UUID4, BaseModel
|
from pydantic import UUID4, BaseModel
|
||||||
from python_multipart import parse_form
|
|
||||||
|
|
||||||
|
from api_gateway import JSONResponse
|
||||||
from boto3clients import dynamodb_client
|
from boto3clients import dynamodb_client
|
||||||
from config import COURSE_TABLE
|
from config import COURSE_TABLE
|
||||||
|
from form_data import parse
|
||||||
|
|
||||||
logger = Logger(__name__)
|
logger = Logger(__name__)
|
||||||
router = Router()
|
router = Router()
|
||||||
@@ -27,45 +31,53 @@ def get_course(course_id: str):
|
|||||||
|
|
||||||
|
|
||||||
class Cert(BaseModel):
|
class Cert(BaseModel):
|
||||||
exp_interval: int
|
exp_interval: int | None = None
|
||||||
rawfile: bytes
|
rawfile: bytes | None = None
|
||||||
|
|
||||||
|
def model_dump(self, **kwargs) -> dict[str, Any]:
|
||||||
|
return super().model_dump(
|
||||||
|
exclude={'rawfile'},
|
||||||
|
exclude_none=True,
|
||||||
|
**kwargs,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class FormData(BaseModel):
|
class Course(BaseModel):
|
||||||
id: UUID4
|
id: UUID4
|
||||||
name: str
|
name: str
|
||||||
access_period: int
|
access_period: int
|
||||||
cert: Cert | None = None
|
cert: Cert | None = None
|
||||||
|
|
||||||
|
|
||||||
@router.post('/<course_id>')
|
@router.put('/<course_id>')
|
||||||
def edit_course(course_id: str):
|
def edit_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')
|
||||||
|
|
||||||
ret = {'id': course_id}
|
|
||||||
body = BytesIO(event.decoded_body.encode())
|
body = BytesIO(event.decoded_body.encode())
|
||||||
|
course = Course.model_validate(
|
||||||
def on_field(field):
|
{'id': course_id} | parse(event.headers, body),
|
||||||
field_name = field.field_name.decode().split('.')
|
|
||||||
|
|
||||||
if len(field_name) > 1:
|
|
||||||
field_name, subfield_name = field_name
|
|
||||||
print(field_name, subfield_name)
|
|
||||||
else:
|
|
||||||
field_name, *_ = field_name
|
|
||||||
ret[field_name] = field.value
|
|
||||||
|
|
||||||
parse_form(
|
|
||||||
event.headers, # type: ignore
|
|
||||||
body,
|
|
||||||
on_field=on_field,
|
|
||||||
on_file=on_field,
|
|
||||||
)
|
)
|
||||||
|
now_ = now()
|
||||||
|
|
||||||
# print(ret.keys())
|
with dyn.transact_writer() as transact:
|
||||||
data = FormData.model_validate(ret)
|
transact.update(
|
||||||
print(data)
|
key=KeyPair(str(course.id), '0'),
|
||||||
return {}
|
update_expr='SET #name = :name, access_period = :access_period, \
|
||||||
|
cert = :cert, updated_at = :updated_at',
|
||||||
|
expr_attr_names={
|
||||||
|
'#name': 'name',
|
||||||
|
},
|
||||||
|
expr_attr_values={
|
||||||
|
':name': course.name,
|
||||||
|
':cert': course.cert.model_dump() if course.cert else None,
|
||||||
|
':access_period': course.access_period,
|
||||||
|
':updated_at': now_,
|
||||||
|
},
|
||||||
|
cond_expr='attribute_exists(sk)',
|
||||||
|
exc_cls=BadRequestError,
|
||||||
|
)
|
||||||
|
|
||||||
|
return JSONResponse(HTTPStatus.NO_CONTENT)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
from http import HTTPMethod, HTTPStatus
|
from http import HTTPMethod, HTTPStatus
|
||||||
|
|
||||||
|
from layercake.dynamodb import DynamoDBPersistenceLayer
|
||||||
from requests_toolbelt import MultipartEncoder
|
from requests_toolbelt import MultipartEncoder
|
||||||
|
|
||||||
from ..conftest import HttpApiProxy, LambdaContext
|
from ..conftest import HttpApiProxy, LambdaContext
|
||||||
@@ -24,14 +25,17 @@ def test_get_course(
|
|||||||
def test_edit_course(
|
def test_edit_course(
|
||||||
app,
|
app,
|
||||||
seeds,
|
seeds,
|
||||||
|
dynamodb_persistence_layer: DynamoDBPersistenceLayer,
|
||||||
http_api_proxy: HttpApiProxy,
|
http_api_proxy: HttpApiProxy,
|
||||||
lambda_context: LambdaContext,
|
lambda_context: LambdaContext,
|
||||||
):
|
):
|
||||||
|
course_id = '2a8963fc-4694-4fe2-953a-316d1b10f1f5'
|
||||||
|
|
||||||
with open('tests/sample.html', 'rb') as f:
|
with open('tests/sample.html', 'rb') as f:
|
||||||
m = MultipartEncoder(
|
m = MultipartEncoder(
|
||||||
fields={
|
fields={
|
||||||
'given_cert': 'true',
|
'given_cert': 'true',
|
||||||
'name': 'pytest',
|
'name': 'pytest updated from test',
|
||||||
'access_period': '365',
|
'access_period': '365',
|
||||||
'cert.exp_interval': '360',
|
'cert.exp_interval': '360',
|
||||||
'cert.rawfile': f,
|
'cert.rawfile': f,
|
||||||
@@ -39,8 +43,8 @@ def test_edit_course(
|
|||||||
)
|
)
|
||||||
r = app.lambda_handler(
|
r = app.lambda_handler(
|
||||||
http_api_proxy(
|
http_api_proxy(
|
||||||
raw_path='/courses/2a8963fc-4694-4fe2-953a-316d1b10f1f5',
|
raw_path=f'/courses/{course_id}',
|
||||||
method=HTTPMethod.POST,
|
method=HTTPMethod.PUT,
|
||||||
headers={
|
headers={
|
||||||
'Content-Type': m.content_type,
|
'Content-Type': m.content_type,
|
||||||
},
|
},
|
||||||
@@ -49,3 +53,10 @@ def test_edit_course(
|
|||||||
),
|
),
|
||||||
lambda_context,
|
lambda_context,
|
||||||
)
|
)
|
||||||
|
assert r['statusCode'] == HTTPStatus.NO_CONTENT
|
||||||
|
|
||||||
|
r = dynamodb_persistence_layer.get_item(
|
||||||
|
key={'id': course_id, 'sk': '0'},
|
||||||
|
)
|
||||||
|
|
||||||
|
print(r)
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ routes = [
|
|||||||
mode = "smart"
|
mode = "smart"
|
||||||
|
|
||||||
[vars]
|
[vars]
|
||||||
ISSUER_URL = "https://58tkjsb308.execute-api.sa-east-1.amazonaws.com"
|
ISSUER_URL = "https://duiolq49qn25e.cloudfront.net"
|
||||||
|
|
||||||
[observability.logs]
|
[observability.logs]
|
||||||
enabled = true
|
enabled = true
|
||||||
|
|||||||
@@ -100,3 +100,35 @@ Resources:
|
|||||||
Path: /userinfo
|
Path: /userinfo
|
||||||
Method: GET
|
Method: GET
|
||||||
ApiId: !Ref HttpApi
|
ApiId: !Ref HttpApi
|
||||||
|
|
||||||
|
OIDCDistribution:
|
||||||
|
Type: AWS::CloudFront::Distribution
|
||||||
|
Properties:
|
||||||
|
DistributionConfig:
|
||||||
|
Enabled: true
|
||||||
|
Origins:
|
||||||
|
- Id: OidcApiOrigin
|
||||||
|
DomainName: !Sub "${HttpApi}.execute-api.${AWS::Region}.amazonaws.com"
|
||||||
|
CustomOriginConfig:
|
||||||
|
OriginProtocolPolicy: https-only
|
||||||
|
DefaultCacheBehavior:
|
||||||
|
TargetOriginId: OidcApiOrigin
|
||||||
|
ViewerProtocolPolicy: redirect-to-https
|
||||||
|
AllowedMethods: [GET, HEAD, OPTIONS, PUT, PATCH, POST, DELETE]
|
||||||
|
CachedMethods: [GET, HEAD, OPTIONS]
|
||||||
|
ForwardedValues:
|
||||||
|
QueryString: true
|
||||||
|
DefaultTTL: 0
|
||||||
|
MinTTL: 0
|
||||||
|
MaxTTL: 0
|
||||||
|
CacheBehaviors:
|
||||||
|
- PathPattern: "/.well-known/*"
|
||||||
|
TargetOriginId: OidcApiOrigin
|
||||||
|
ViewerProtocolPolicy: redirect-to-https
|
||||||
|
AllowedMethods: [GET, HEAD, OPTIONS]
|
||||||
|
CachedMethods: [GET, HEAD, OPTIONS]
|
||||||
|
ForwardedValues:
|
||||||
|
QueryString: false
|
||||||
|
DefaultTTL: 3600 # 1 hora
|
||||||
|
MinTTL: 300 # 5 min
|
||||||
|
MaxTTL: 86400 # 1 dia
|
||||||
|
|||||||
Reference in New Issue
Block a user