-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #330 from NHSDigital/AMB-2382_audit_table_and_file…
…_movement_refactor AMB-2382_audit_table_and_file_movement_refactor
- Loading branch information
Showing
53 changed files
with
1,742 additions
and
1,714 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,67 +1,49 @@ | ||
"""Add the filename to the audit table and check for duplicates.""" | ||
|
||
import os | ||
|
||
from typing import Union | ||
from boto3.dynamodb.conditions import Key,Attr | ||
from boto3.dynamodb.conditions import Key | ||
from clients import dynamodb_client, dynamodb_resource, logger | ||
from errors import UnhandledAuditTableError | ||
|
||
|
||
def update_audit_table_status(file_key: str) -> str: | ||
from constants import AUDIT_TABLE_NAME, AUDIT_TABLE_QUEUE_NAME_GSI, FileStatus, AuditTableKeys | ||
|
||
|
||
def get_next_queued_file_details(queue_name: str) -> Union[dict, None]: | ||
""" | ||
Update the status in the audit table. | ||
Checks for queued files. | ||
Returns a dictionary containing the details of the oldest queued file, or returns None if no queued files are found. | ||
""" | ||
queued_files_found_in_audit_table: dict = dynamodb_resource.Table(AUDIT_TABLE_NAME).query( | ||
IndexName=AUDIT_TABLE_QUEUE_NAME_GSI, | ||
KeyConditionExpression=Key(AuditTableKeys.QUEUE_NAME).eq(queue_name) | ||
& Key(AuditTableKeys.STATUS).eq(FileStatus.QUEUED), | ||
) | ||
|
||
queued_files_details: list = queued_files_found_in_audit_table["Items"] | ||
|
||
# Return the oldest queued file | ||
return sorted(queued_files_details, key=lambda x: x["timestamp"])[0] if queued_files_details else None | ||
|
||
|
||
def change_audit_table_status_to_processed(file_key: str, message_id: str) -> None: | ||
"""Updates the status in the audit table to 'Processed' and returns the queue name.""" | ||
try: | ||
table_name = os.environ["AUDIT_TABLE_NAME"] | ||
file_name_gsi = "filename_index" | ||
file_name_response = dynamodb_resource.Table(table_name).query( | ||
IndexName=file_name_gsi, KeyConditionExpression=Key("filename").eq(file_key) | ||
) | ||
items = file_name_response.get("Items", []) | ||
message_id = items[0].get("message_id") | ||
queue_name = items[0].get("queue_name") | ||
# Add to the audit table | ||
# Update the status in the audit table to "Processed" | ||
dynamodb_client.update_item( | ||
TableName=table_name, | ||
Key={"message_id": {"S": message_id}}, | ||
TableName=AUDIT_TABLE_NAME, | ||
Key={AuditTableKeys.MESSAGE_ID: {"S": message_id}}, | ||
UpdateExpression="SET #status = :status", | ||
ExpressionAttributeNames={"#status": "status"}, | ||
ExpressionAttributeValues={":status": {"S": "Processed"}}, | ||
ConditionExpression="attribute_exists(message_id)" | ||
ExpressionAttributeValues={":status": {"S": FileStatus.PROCESSED}}, | ||
ConditionExpression="attribute_exists(message_id)", | ||
) | ||
logger.info("%s file, with message id %s, and the status successfully updated to audit table", file_key, message_id) | ||
return queue_name | ||
except Exception as error: # pylint: disable = broad-exception-caught | ||
error_message = error #f"Error adding {file_key} to the audit table" | ||
logger.error(error_message) | ||
raise UnhandledAuditTableError(error_message) from error | ||
|
||
def get_queued_file_details(queue_name: str) -> tuple[Union[None,str],Union[None,str]]: | ||
""" | ||
Check for queued files which return none or oldest file queued for processing. | ||
Returns a tuple in the format (file_name, message_id) for the oldest file. | ||
Defaults to (none,none) if no file found in queued status | ||
""" | ||
table_name = os.environ["AUDIT_TABLE_NAME"] | ||
queue_name_gsi = "queue_name_index" | ||
|
||
queue_response = dynamodb_resource.Table(table_name).query( | ||
IndexName=queue_name_gsi, | ||
KeyConditionExpression=Key("queue_name").eq(queue_name) | ||
& Key("status").eq("Queued"), | ||
logger.info( | ||
"The status of %s file, with message id %s, was successfully updated to %s in the audit table", | ||
file_key, | ||
message_id, | ||
FileStatus.PROCESSED, | ||
) | ||
if queue_response["Items"]: | ||
file_name, message_id = get_file_name(queue_response) | ||
return file_name, message_id | ||
else: | ||
return None, None | ||
|
||
def get_file_name(queue_response: dict) -> tuple[str,str]: | ||
""" | ||
Returns (file_name, message_id) for the oldest file. | ||
""" | ||
sorted_item = sorted(queue_response["Items"], key=lambda x: x["timestamp"]) | ||
first_record = sorted_item[0] | ||
file_name = first_record.get("filename") | ||
message_id = first_record.get("message_id") | ||
return file_name, message_id | ||
except Exception as error: # pylint: disable = broad-exception-caught | ||
logger.error(error) | ||
raise UnhandledAuditTableError(error) from error |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,22 +1,49 @@ | ||
"""Constants for ack lambda""" | ||
|
||
import os | ||
from utils_for_ack_lambda import get_environment | ||
|
||
class Constants: | ||
"""Constants for ack lambda""" | ||
|
||
ack_headers = [ | ||
"MESSAGE_HEADER_ID", | ||
"HEADER_RESPONSE_CODE", | ||
"ISSUE_SEVERITY", | ||
"ISSUE_CODE", | ||
"ISSUE_DETAILS_CODE", | ||
"RESPONSE_TYPE", | ||
"RESPONSE_CODE", | ||
"RESPONSE_DISPLAY", | ||
"RECEIVED_TIME", | ||
"MAILBOX_FROM", | ||
"LOCAL_ID", | ||
"IMMS_ID", | ||
"OPERATION_OUTCOME", | ||
"MESSAGE_DELIVERY", | ||
] | ||
ENVIRONMENT = get_environment() | ||
SOURCE_BUCKET_NAME = f"immunisation-batch-{ENVIRONMENT}-data-sources" | ||
ACK_BUCKET_NAME = os.getenv("ACK_BUCKET_NAME") | ||
AUDIT_TABLE_NAME = os.getenv("AUDIT_TABLE_NAME") | ||
AUDIT_TABLE_FILENAME_GSI = "filename_index" | ||
AUDIT_TABLE_QUEUE_NAME_GSI = "queue_name_index" | ||
FILE_NAME_PROC_LAMBDA_NAME = os.getenv("FILE_NAME_PROC_LAMBDA_NAME") | ||
|
||
|
||
class FileStatus: | ||
"""File status constants""" | ||
|
||
QUEUED = "Queued" | ||
PROCESSING = "Processing" | ||
PROCESSED = "Processed" | ||
DUPLICATE = "Duplicate" | ||
|
||
|
||
class AuditTableKeys: | ||
"""Audit table keys""" | ||
|
||
FILENAME = "filename" | ||
MESSAGE_ID = "message_id" | ||
QUEUE_NAME = "queue_name" | ||
STATUS = "status" | ||
TIMESTAMP = "timestamp" | ||
|
||
|
||
ACK_HEADERS = [ | ||
"MESSAGE_HEADER_ID", | ||
"HEADER_RESPONSE_CODE", | ||
"ISSUE_SEVERITY", | ||
"ISSUE_CODE", | ||
"ISSUE_DETAILS_CODE", | ||
"RESPONSE_TYPE", | ||
"RESPONSE_CODE", | ||
"RESPONSE_DISPLAY", | ||
"RECEIVED_TIME", | ||
"MAILBOX_FROM", | ||
"LOCAL_ID", | ||
"IMMS_ID", | ||
"OPERATION_OUTCOME", | ||
"MESSAGE_DELIVERY", | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,29 +1,5 @@ | ||
"""Custom exceptions for the Filename Processor.""" | ||
|
||
|
||
class DuplicateFileError(Exception): | ||
"""A custom exception for when it is identified that the file is a duplicate.""" | ||
|
||
|
||
class ProcessingError(Exception): | ||
"""A custom exception for when it is identified that supplier_vaccine file is under processing""" | ||
"""Custom exceptions for the Ack lambda.""" | ||
|
||
|
||
class UnhandledAuditTableError(Exception): | ||
"""A custom exception for when an unexpected error occurs whilst adding the file to the audit table.""" | ||
|
||
|
||
class VaccineTypePermissionsError(Exception): | ||
"""A custom exception for when the supplier does not have the necessary vaccine type permissions.""" | ||
|
||
|
||
class InvalidFileKeyError(Exception): | ||
"""A custom exception for when the file key is invalid.""" | ||
|
||
|
||
class InvalidSupplierError(Exception): | ||
"""A custom exception for when the supplier has not been correctly identified.""" | ||
|
||
|
||
class UnhandledSqsError(Exception): | ||
"""A custom exception for when an unexpected error occurs whilst sending a message to SQS.""" |
Oops, something went wrong.