Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 29 additions & 30 deletions infrastructure/instance/.terraform.lock.hcl

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ environment = "dev"
immunisation_account_id = "345594581768"
dspp_core_account_id = "603871901111"
pds_environment = "int"
mns_environment = "int"
mns_environment = "dev"
error_alarm_notifications_enabled = true
create_mesh_processor = false
has_sub_environment_scope = true
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ environment = "dev"
immunisation_account_id = "345594581768"
dspp_core_account_id = "603871901111"
pds_environment = "int"
mns_environment = "int"
mns_environment = "dev"
error_alarm_notifications_enabled = false
mns_publisher_feature_enabled = true
create_mesh_processor = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ environment = "dev"
immunisation_account_id = "345594581768"
dspp_core_account_id = "603871901111"
pds_environment = "int"
mns_environment = "int"
mns_environment = "dev"
error_alarm_notifications_enabled = false
mns_publisher_feature_enabled = true # Switch this off once tested fully e2e in Lambda branch
create_mesh_processor = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ environment = "dev"
immunisation_account_id = "345594581768"
dspp_core_account_id = "603871901111"
pds_environment = "ref"
mns_environment = "int"
mns_environment = "dev"
error_alarm_notifications_enabled = true
create_mesh_processor = false
has_sub_environment_scope = true
2 changes: 2 additions & 0 deletions infrastructure/instance/mns_publisher.tf
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ module "mns_publisher" {
enable_lambda_alarm = var.error_alarm_notifications_enabled # consider just INT and PROD
immunisation_account_id = var.immunisation_account_id
is_temp = local.is_temp
enable_mns_test_queue = var.mns_environment == "dev"
resource_scope = local.resource_scope
imms_base_path = strcontains(var.sub_environment, "pr-") ? "immunisation-fhir-api/FHIR/R4-${var.sub_environment}" : "immunisation-fhir-api/FHIR/R4"
lambda_kms_encryption_key_arn = data.aws_kms_key.existing_lambda_encryption_key.arn
mns_publisher_resource_name_prefix = "${local.resource_scope}-mns-outbound-events"
mns_test_notification_name_prefix = "${local.resource_scope}-mns-test-notification"
secrets_manager_policy_path = "${local.policy_path}/secret_manager.json"
account_id = data.aws_caller_identity.current.account_id
pds_environment = var.pds_environment
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ resource "aws_lambda_function" "mns_publisher_lambda" {
environment {
variables = {
SPLUNK_FIREHOSE_NAME = var.splunk_firehose_stream_name
MNS_TEST_QUEUE_URL = var.enable_mns_test_queue ? aws_sqs_queue.mns_test_notification[0].url : ""
IMMUNIZATION_ENV = var.resource_scope,
IMMUNIZATION_BASE_PATH = var.imms_base_path
PDS_ENV = var.pds_environment
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
resource "aws_sqs_queue" "mns_test_notification" {
Comment thread
dlzhry2nhs marked this conversation as resolved.
count = var.enable_mns_test_queue ? 1 : 0
name = "${var.mns_test_notification_name_prefix}-queue"
fifo_queue = false
message_retention_seconds = 86400
visibility_timeout_seconds = 300
}


data "aws_iam_policy_document" "mns_test_notification_sqs_policy" {
count = var.enable_mns_test_queue ? 1 : 0
statement {
sid = "mns-test-notification-allow-lambda-access"
effect = "Allow"

principals {
type = "AWS"
identifiers = [aws_iam_role.mns_publisher_lambda_exec_role.arn]
Comment thread
dlzhry2nhs marked this conversation as resolved.
}

actions = [
"sqs:SendMessage",
]

resources = [
aws_sqs_queue.mns_test_notification[0].arn
Comment thread
dlzhry2nhs marked this conversation as resolved.
]
}
}

resource "aws_sqs_queue_policy" "mns_test_notification_sqs" {
count = var.enable_mns_test_queue ? 1 : 0
queue_url = aws_sqs_queue.mns_test_notification[0].id
policy = data.aws_iam_policy_document.mns_test_notification_sqs_policy[0].json
}

output "mns_test_queue_url" {
value = var.enable_mns_test_queue ? aws_sqs_queue.mns_test_notification[0].url : null
description = "URL of the MNS test notifications queue"
}

output "mns_test_queue_arn" {
value = var.enable_mns_test_queue ? aws_sqs_queue.mns_test_notification[0].arn : null
description = "ARN of the MNS test notifications queue"
}
11 changes: 11 additions & 0 deletions infrastructure/instance/modules/mns_publisher/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -102,4 +102,15 @@ variable "account_id" {
variable "secrets_manager_policy_path" {
type = string
description = "Path to the IAM policy JSON template for Secrets Manager access (e.g., ./policies/secret_manager.json)."
}

variable "mns_test_notification_name_prefix" {
type = string
description = "The prefix for the name of resources for testing mns notification"
}

variable "enable_mns_test_queue" {
description = "Enable test SQS queue for MNS notifications (dev only)"
type = bool
default = false
}
10 changes: 10 additions & 0 deletions infrastructure/instance/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,13 @@ output "id_sync_queue_arn" {
description = "The ARN of the ID Sync (MNS NHS Number change) SQS queue"
value = aws_sqs_queue.id_sync_queue.arn
}

output "mns_test_queue_url" {
value = var.mns_publisher_feature_enabled ? module.mns_publisher[0].mns_test_queue_url : null
description = "URL of the MNS test notifications queue (from mns_publisher module)"
}

output "mns_test_queue_arn" {
value = var.mns_publisher_feature_enabled ? module.mns_publisher[0].mns_test_queue_arn : null
description = "ARN of the MNS test notifications queue (from mns_publisher module)"
}
1 change: 0 additions & 1 deletion infrastructure/instance/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@ variable "mns_publisher_feature_enabled" {
description = "Switch to the MNS Publisher feature which allows us to publish Immunisation events."
type = bool
}

variable "has_sub_environment_scope" {
description = "True if the sub-environment is a standalone environment, e.g. internal-dev. False if it is part of a blue-green split, e.g. int-green."
type = bool
Expand Down
28 changes: 0 additions & 28 deletions lambdas/mns_publisher/src/constants.py
Original file line number Diff line number Diff line change
@@ -1,33 +1,5 @@
from typing import TypedDict

# Static constants for the MNS notification creation process
SPEC_VERSION = "1.0"
IMMUNISATION_TYPE = "imms-vaccinations-1"


# Fields from the incoming SQS message that forms part of the base schema and filtering attributes for MNS notifications
class FilteringData(TypedDict):
"""MNS notification filtering attributes."""

generalpractitioner: str | None
sourceorganisation: str
sourceapplication: str
subjectage: int
immunisationtype: str
action: str


class MnsNotificationPayload(TypedDict):
"""CloudEvents-compliant MNS notification payload."""

specversion: str
id: str
source: str
type: str
time: str
subject: str
dataref: str
filtering: FilteringData


DYNAMO_DB_TYPE_DESCRIPTORS = ("S", "N", "BOOL", "M", "L")
3 changes: 2 additions & 1 deletion lambdas/mns_publisher/src/create_notification.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@

from aws_lambda_typing.events.sqs import SQSMessage

from common.api_clients.constants import MnsNotificationPayload
from common.api_clients.get_pds_details import pds_get_patient_details
from common.clients import logger
from common.get_service_url import get_service_url
from constants import DYNAMO_DB_TYPE_DESCRIPTORS, IMMUNISATION_TYPE, SPEC_VERSION, MnsNotificationPayload
from constants import DYNAMO_DB_TYPE_DESCRIPTORS, IMMUNISATION_TYPE, SPEC_VERSION

IMMUNIZATION_ENV = os.getenv("IMMUNIZATION_ENV")
IMMUNIZATION_BASE_PATH = os.getenv("IMMUNIZATION_BASE_PATH")
Expand Down
6 changes: 3 additions & 3 deletions lambdas/mns_publisher/src/process_records.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@

from common.api_clients.mns_service import MnsService
from common.api_clients.mns_setup import get_mns_service
from common.api_clients.mock_mns_service import MockMnsService
from common.clients import logger
from create_notification import create_mns_notification

mns_env = os.getenv("MNS_ENV", "int")
MNS_TEST_QUEUE_URL = os.getenv("MNS_TEST_QUEUE_URL")


def process_records(records: list[SQSMessage]) -> dict[str, list]:
Expand Down Expand Up @@ -37,7 +39,7 @@ def process_records(records: list[SQSMessage]) -> dict[str, list]:
return {"batchItemFailures": batch_item_failures}


def process_record(record: SQSMessage, mns_service: MnsService) -> None:
def process_record(record: SQSMessage, mns_service: MnsService | MockMnsService) -> None:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potentially even better if we implement an abstract class, or vanilla Python base class that defines the interface that includes a publish_notification(self, notification: MnsPayloadBla) -> None in the contract. Optional though, this is good enough for now...maybe.

"""
Process a single SQS record.
Args:
Expand Down Expand Up @@ -65,8 +67,6 @@ def process_record(record: SQSMessage, mns_service: MnsService) -> None:
mns_service.publish_notification(mns_notification_payload)
logger.info("Successfully created MNS notification", extra={"mns_notification_id": notification_id})

return None


def extract_trace_ids(record: SQSMessage) -> Tuple[str, str | None]:
"""
Expand Down
29 changes: 29 additions & 0 deletions lambdas/shared/src/common/api_clients/constants.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
from typing import TypedDict

"""Constants used by API clients"""

DEV_ENVIRONMENT = "dev"


class Constants:
"""Constants used for the API clients"""
Expand All @@ -8,3 +12,28 @@ class Constants:
DEFAULT_API_CLIENTS_TIMEOUT = 5
API_CLIENTS_MAX_RETRIES = 2
API_CLIENTS_BACKOFF_SECONDS = 0.5


# Fields from the incoming SQS message that forms part of the base schema and filtering attributes for MNS notifications
class FilteringData(TypedDict):
"""MNS notification filtering attributes."""

generalpractitioner: str | None
sourceorganisation: str
sourceapplication: str
subjectage: int
immunisationtype: str
action: str


class MnsNotificationPayload(TypedDict):
"""CloudEvents-compliant MNS notification payload."""

specversion: str
id: str
source: str
type: str
time: str
subject: str
dataref: str
filtering: FilteringData
3 changes: 2 additions & 1 deletion lambdas/shared/src/common/api_clients/mns_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import requests

from common.api_clients.authentication import AppRestrictedAuth
from common.api_clients.constants import MnsNotificationPayload
from common.api_clients.errors import raise_error_response
from common.api_clients.retry import request_with_retry_backoff

Expand Down Expand Up @@ -136,7 +137,7 @@ def check_delete_subscription(self):
except Exception as e:
return f"Error deleting subscription: {str(e)}"

def publish_notification(self, notification_payload) -> dict | None:
def publish_notification(self, notification_payload: MnsNotificationPayload) -> dict | None:
response = requests.request(
"POST",
f"{MNS_BASE_URL}/events",
Expand Down
Loading
Loading