fix enroll and reenroll relationship
This commit is contained in:
@@ -1,4 +1,7 @@
|
||||
from abc import ABC
|
||||
from dataclasses import dataclass
|
||||
from datetime import timedelta
|
||||
from enum import Enum
|
||||
from typing import NotRequired, Self, TypedDict
|
||||
|
||||
from layercake.dateutils import now, ttl
|
||||
@@ -32,15 +35,16 @@ Subscription = TypedDict(
|
||||
)
|
||||
|
||||
|
||||
class LinkedEntity(str):
|
||||
def __new__(cls, id: str, type: str) -> Self:
|
||||
return super().__new__(cls, '#'.join([type.lower(), id]))
|
||||
class Kind(str, Enum):
|
||||
ORDER = 'ORDER'
|
||||
ENROLLMENT = 'ENROLLMENT'
|
||||
|
||||
def __init__(self, id: str, type: str) -> None:
|
||||
# __init__ is used to store the parameters for later reference.
|
||||
# For immutable types like str, __init__ cannot change the instance's value.
|
||||
self.id = id
|
||||
self.type = type
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class LinkedEntity(ABC):
|
||||
id: str
|
||||
kind: Kind
|
||||
table_name: str | None = None
|
||||
|
||||
|
||||
class DeduplicationConflictError(Exception):
|
||||
@@ -76,29 +80,27 @@ def enroll(
|
||||
)
|
||||
|
||||
# Relationships between this enrollment and its related entities
|
||||
for parent_entity in linked_entities:
|
||||
perent_id = parent_entity.id
|
||||
entity_sk = f'LINKED_ENTITIES#{parent_entity.type}'
|
||||
keyprefix = parent_entity.type.lower()
|
||||
|
||||
for entity in linked_entities:
|
||||
# Parent knows the child
|
||||
transact.put(
|
||||
item={
|
||||
'id': perent_id,
|
||||
'sk': f'{entity_sk}#CHILD',
|
||||
'id': entity.id,
|
||||
'sk': f'LINKED_ENTITIES#{entity.kind.value}#CHILD',
|
||||
'created_at': now_,
|
||||
f'{keyprefix}_id': enrollment.id,
|
||||
'enrollment_id': enrollment.id,
|
||||
},
|
||||
cond_expr='attribute_not_exists(sk)',
|
||||
table_name=entity.table_name,
|
||||
)
|
||||
|
||||
keyprefix = entity.kind.value.lower()
|
||||
# Child knows the parent
|
||||
transact.put(
|
||||
item={
|
||||
'id': enrollment.id,
|
||||
'sk': f'{entity_sk}#PARENT',
|
||||
'sk': f'LINKED_ENTITIES#{entity.kind.value}#PARENT',
|
||||
'created_at': now_,
|
||||
f'{keyprefix}_id': perent_id,
|
||||
f'{keyprefix}_id': entity.id,
|
||||
},
|
||||
cond_expr='attribute_not_exists(sk)',
|
||||
)
|
||||
|
||||
@@ -81,6 +81,7 @@ def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool:
|
||||
class Course:
|
||||
id: str
|
||||
name: str
|
||||
access_period: int
|
||||
|
||||
|
||||
def _get_courses(ids: set) -> tuple[Course, ...]:
|
||||
@@ -93,6 +94,7 @@ def _get_courses(ids: set) -> tuple[Course, ...]:
|
||||
Course(
|
||||
id=idx,
|
||||
name=obj['name'],
|
||||
access_period=obj['access_period'],
|
||||
)
|
||||
for idx, obj in result.items()
|
||||
)
|
||||
|
||||
@@ -18,7 +18,11 @@ from layercake.dynamodb import (
|
||||
|
||||
from boto3clients import dynamodb_client
|
||||
from config import COURSE_TABLE, ENROLLMENT_TABLE, ORDER_TABLE
|
||||
from enrollment import LinkedEntity, enroll
|
||||
from enrollment import (
|
||||
Kind,
|
||||
LinkedEntity,
|
||||
enroll,
|
||||
)
|
||||
from schemas import Course, Enrollment, User
|
||||
|
||||
logger = Logger(__name__)
|
||||
@@ -89,7 +93,15 @@ def _handler(record: Course, context: dict) -> Enrollment:
|
||||
enrollment,
|
||||
persistence_layer=enrollment_layer,
|
||||
deduplication_window={'offset_days': 90},
|
||||
linked_entities=frozenset({LinkedEntity(context['order_id'], 'ORDER')}),
|
||||
linked_entities=frozenset(
|
||||
{
|
||||
LinkedEntity(
|
||||
id=context['order_id'],
|
||||
kind=Kind.ORDER,
|
||||
table_name=ORDER_TABLE,
|
||||
),
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
return enrollment
|
||||
|
||||
@@ -56,6 +56,9 @@ def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool:
|
||||
)
|
||||
|
||||
try:
|
||||
if 's3_uri' not in cert:
|
||||
raise ValueError('Template URI is missing')
|
||||
|
||||
# Send template URI and data to Paperforge API to generate a PDF
|
||||
r = requests.post(
|
||||
PAPERFORGE_API,
|
||||
@@ -79,10 +82,11 @@ def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool:
|
||||
),
|
||||
},
|
||||
),
|
||||
timeout=5,
|
||||
)
|
||||
r.raise_for_status()
|
||||
|
||||
object_key = f'issuedcerts/{enrollment_id}.pdf'
|
||||
object_key = f'certs/{enrollment_id}.pdf'
|
||||
s3_uri = f's3://{BUCKET_NAME}/{object_key}'
|
||||
|
||||
s3_client.put_object(
|
||||
@@ -93,10 +97,10 @@ def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool:
|
||||
)
|
||||
|
||||
logger.debug(f'PDF uploaded successfully to {s3_uri}')
|
||||
except KeyError:
|
||||
except ValueError as exc:
|
||||
# PDF generation fails if template URI is missing
|
||||
s3_uri = None
|
||||
logger.debug('Template URI is missing')
|
||||
logger.exception(exc)
|
||||
except requests.exceptions.RequestException as exc:
|
||||
logger.exception(exc)
|
||||
raise
|
||||
|
||||
@@ -10,7 +10,7 @@ from layercake.dynamodb import DynamoDBPersistenceLayer, SortKey, TransactKey
|
||||
|
||||
from boto3clients import dynamodb_client
|
||||
from config import ENROLLMENT_TABLE
|
||||
from enrollment import LinkedEntity, enroll
|
||||
from enrollment import Kind, LinkedEntity, enroll
|
||||
from schemas import Course, Enrollment, User
|
||||
|
||||
logger = Logger(__name__)
|
||||
@@ -52,7 +52,12 @@ def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool:
|
||||
'offset_days': metadata['dedup_window_offset_days'],
|
||||
},
|
||||
linked_entities=frozenset(
|
||||
{LinkedEntity(new_image['id'], 'ENROLLMENT')},
|
||||
{
|
||||
LinkedEntity(
|
||||
id=new_image['id'],
|
||||
kind=Kind.ENROLLMENT,
|
||||
),
|
||||
},
|
||||
),
|
||||
persistence_layer=dyn,
|
||||
)
|
||||
|
||||
@@ -29,8 +29,7 @@ def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool:
|
||||
pk=old_image['id'],
|
||||
sk='0',
|
||||
),
|
||||
update_expr='SET access_expired = :true, \
|
||||
updated_at = :now',
|
||||
update_expr='SET access_expired = :true, updated_at = :now',
|
||||
expr_attr_values={
|
||||
':true': True,
|
||||
':now': now_,
|
||||
|
||||
@@ -308,6 +308,7 @@ Resources:
|
||||
Type: AWS::Serverless::Function
|
||||
Properties:
|
||||
Handler: events.issue_cert.lambda_handler
|
||||
# Timeout: 30
|
||||
LoggingConfig:
|
||||
LogGroup: !Ref EventLog
|
||||
Policies:
|
||||
@@ -331,10 +332,10 @@ Resources:
|
||||
old_image:
|
||||
status: [IN_PROGRESS]
|
||||
|
||||
EventCertReportingAppendCertFunction:
|
||||
EventReportingAppendCertFunction:
|
||||
Type: AWS::Serverless::Function
|
||||
Properties:
|
||||
Handler: events.cert_reporting.append_cert.lambda_handler
|
||||
Handler: events.reporting.append_cert.lambda_handler
|
||||
LoggingConfig:
|
||||
LogGroup: !Ref EventLog
|
||||
Policies:
|
||||
@@ -346,23 +347,19 @@ Resources:
|
||||
Properties:
|
||||
Pattern:
|
||||
resources: [!Ref EnrollmentTable]
|
||||
detail-type: [MODIFY]
|
||||
detail:
|
||||
keys:
|
||||
sk: ["0"]
|
||||
new_image:
|
||||
status: [COMPLETED]
|
||||
cert:
|
||||
exists: true
|
||||
org_id:
|
||||
exists: true
|
||||
old_image:
|
||||
cert:
|
||||
exists: false
|
||||
- exists: true
|
||||
|
||||
EventCertReportingSendReportEmailFunction:
|
||||
EventReportingSendReportEmailFunction:
|
||||
Type: AWS::Serverless::Function
|
||||
Properties:
|
||||
Handler: events.cert_reporting.send_report_email.lambda_handler
|
||||
Handler: events.reporting.send_report_email.lambda_handler
|
||||
LoggingConfig:
|
||||
LogGroup: !Ref EventLog
|
||||
Policies:
|
||||
|
||||
@@ -20,6 +20,7 @@ def pytest_configure():
|
||||
os.environ['ORDER_TABLE'] = PYTEST_TABLE_NAME
|
||||
os.environ['ENROLLMENT_TABLE'] = PYTEST_TABLE_NAME
|
||||
os.environ['BUCKET_NAME'] = 'saladeaula.digital'
|
||||
os.environ['LOG_LEVEL'] = 'DEBUG'
|
||||
|
||||
|
||||
@dataclass
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from datetime import timedelta
|
||||
|
||||
import app.events.cert_reporting.append_cert as app
|
||||
import app.events.reporting.append_cert as app
|
||||
from aws_lambda_powertools.utilities.typing import LambdaContext
|
||||
from layercake.dateutils import now
|
||||
from layercake.dynamodb import (
|
||||
@@ -1,4 +1,4 @@
|
||||
import app.events.cert_reporting.send_report_email as app
|
||||
import app.events.reporting.send_report_email as app
|
||||
from aws_lambda_powertools.utilities.typing import LambdaContext
|
||||
from layercake.dynamodb import (
|
||||
DynamoDBPersistenceLayer,
|
||||
@@ -1,6 +1,6 @@
|
||||
import app.events.enroll as app
|
||||
from aws_lambda_powertools.utilities.typing import LambdaContext
|
||||
from layercake.dynamodb import DynamoDBPersistenceLayer
|
||||
from layercake.dynamodb import DynamoDBPersistenceLayer, KeyPair
|
||||
|
||||
|
||||
def test_enroll(
|
||||
@@ -8,12 +8,26 @@ def test_enroll(
|
||||
dynamodb_persistence_layer: DynamoDBPersistenceLayer,
|
||||
lambda_context: LambdaContext,
|
||||
):
|
||||
order_id = 'cpYSbBcie2NDbZhDKCxCih'
|
||||
event = {
|
||||
'detail': {
|
||||
'new_image': {
|
||||
'id': 'cpYSbBcie2NDbZhDKCxCih',
|
||||
'id': order_id,
|
||||
'sk': 'generated_items',
|
||||
}
|
||||
}
|
||||
}
|
||||
assert app.lambda_handler(event, lambda_context) # type: ignore
|
||||
|
||||
# Parent knows the child
|
||||
order_entity = dynamodb_persistence_layer.collection.get_item(
|
||||
KeyPair(order_id, 'LINKED_ENTITIES#ORDER#CHILD')
|
||||
)
|
||||
assert order_entity
|
||||
|
||||
# Child knows the parent
|
||||
enrollment_entity = dynamodb_persistence_layer.collection.get_item(
|
||||
KeyPair(order_entity['enrollment_id'], 'LINKED_ENTITIES#ORDER#PARENT'),
|
||||
)
|
||||
|
||||
assert enrollment_entity['order_id'] == order_id
|
||||
|
||||
@@ -30,19 +30,19 @@ def test_reenroll(
|
||||
assert app.lambda_handler(event, lambda_context) # type: ignore
|
||||
|
||||
# Parent knows the child
|
||||
child_entity = dynamodb_persistence_layer.collection.get_item(
|
||||
current_entity = dynamodb_persistence_layer.collection.get_item(
|
||||
KeyPair(
|
||||
pk=parent_id,
|
||||
sk='LINKED_ENTITIES#ENROLLMENT#CHILD',
|
||||
)
|
||||
)
|
||||
assert child_entity
|
||||
assert current_entity
|
||||
|
||||
# Child knows the parent
|
||||
parent_entity = dynamodb_persistence_layer.collection.get_item(
|
||||
new_entity = dynamodb_persistence_layer.collection.get_item(
|
||||
KeyPair(
|
||||
pk=child_entity['enrollment_id'],
|
||||
pk=current_entity['enrollment_id'],
|
||||
sk='LINKED_ENTITIES#ENROLLMENT#PARENT',
|
||||
)
|
||||
)
|
||||
assert parent_entity['enrollment_id'] == parent_id
|
||||
assert new_entity['enrollment_id'] == parent_id
|
||||
|
||||
Reference in New Issue
Block a user