from datetime import datetime from uuid import uuid4 from aws_lambda_powertools import Logger from aws_lambda_powertools.utilities.data_classes import ( EventBridgeEvent, event_source, ) from aws_lambda_powertools.utilities.typing import LambdaContext from layercake.dateutils import now from layercake.dynamodb import DynamoDBPersistenceLayer, KeyPair from boto3clients import dynamodb_client from config import ENROLLMENT_TABLE, ORDER_TABLE from enrollment import ( Enrollment, Kind, LinkedEntity, Seat, Subscription, enroll, ) logger = Logger(__name__) dyn = DynamoDBPersistenceLayer(ENROLLMENT_TABLE, dynamodb_client) @event_source(data_class=EventBridgeEvent) @logger.inject_lambda_context def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool: old_image = event.detail['old_image'] now_ = now() # Key pattern `SCHEDULED#ORG#{org_id}` *_, org_id = old_image['id'].split('#') # Key pattern `{YYYY-MM-DD HH:MM:SS.mmmmmm}#{lock_hash}` sk = old_image['sk'] _, lock_hash = sk.split('#') offset_days = old_image.get('dedup_window_offset_days') billing_day = old_image.get('subscription_billing_day') created_by = old_image.get('created_by') seat: Seat = old_image.get('seat', {}) enrollment = Enrollment( id=old_image['enrollment_id'], course=old_image['course'], user=old_image['user'], ) subscription: Subscription | None = ( { 'org_id': org_id, 'billing_day': int(billing_day), } if billing_day else None ) try: # The enrollment must know its source linked_entities = ( frozenset( { LinkedEntity( id=seat['order_id'], kind=Kind.ORDER, table_name=ORDER_TABLE, ), }, ) if seat else frozenset() ) enroll( enrollment, org={ 'org_id': org_id, 'name': old_image['org_name'], }, subscription=subscription, cancel_policy=bool(subscription), created_by=created_by, seat=seat, scheduled_at=datetime.fromisoformat(old_image['scheduled_at']), # Transfer the deduplication window if it exists deduplication_window={'offset_days': offset_days} if offset_days else None, linked_entities=linked_entities, persistence_layer=dyn, ) with dyn.transact_writer() as transact: transact.put( item={ 'id': old_image['id'], 'sk': f'{sk}#EXECUTED', 'enrollment_id': enrollment.id, 'user': old_image['user'], 'course': old_image['course'], 'created_by': created_by, 'created_at': now_, } ) transact.delete( key=KeyPair( pk='LOCK#SCHEDULED', sk=lock_hash, ), ) except Exception as exc: dyn.put_item( item={ 'id': old_image['id'], 'sk': f'{sk}#FAILED', 'cause': { 'type': type(exc).__name__, 'message': str(exc), }, 'snapshot': old_image, 'created_at': now_, } ) return True