Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 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
1 change: 1 addition & 0 deletions infrastructure/instance/mns_publisher.tf
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ module "mns_publisher" {
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 = aws_sqs_queue.mns_test_notification[0].url
Comment thread
dlzhry2nhs marked this conversation as resolved.
Outdated
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,46 @@
resource "aws_sqs_queue" "mns_test_notification" {
Comment thread
dlzhry2nhs marked this conversation as resolved.
count = var.mns_environment == "dev" ? 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.mns_environment == "dev" ? 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.mns_environment == "dev" ? 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.mns_environment == "dev" ? aws_sqs_queue.mns_test_notification[0].url : null
description = "URL of the MNS test notifications queue"
}

output "mns_test_queue_arn" {
value = var.mns_environment == "dev" ? aws_sqs_queue.mns_test_notification[0].arn : 0
description = "ARN of the MNS test notifications queue"
}

5 changes: 5 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,9 @@ 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"
}
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
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
30 changes: 18 additions & 12 deletions lambdas/shared/src/common/api_clients/mns_setup.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,31 @@
import logging
import os

import boto3
from botocore.config import Config

from common.api_clients.authentication import AppRestrictedAuth, Service
from common.api_clients.mns_service import MnsService
from common.api_clients.mock_mns_service import MockMnsService
from common.cache import Cache

logging.basicConfig(level=logging.INFO)
MNS_TEST_QUEUE_URL = os.getenv("MNS_TEST_QUEUE_URL")


def get_mns_service(mns_env: str = "int"):
boto_config = Config(region_name="eu-west-2")
cache = Cache(directory="/tmp") # NOSONAR(S5443)
logging.info("Creating authenticator...")
authenticator = AppRestrictedAuth(
service=Service.PDS,
secret_manager_client=boto3.client("secretsmanager", config=boto_config),
environment=mns_env,
cache=cache,
)

logging.info("Authentication Initiated...")
return MnsService(authenticator)
if mns_env == "dev":
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.

For flexibility, might it be better to have pass in a flag for use_mock? Could easily be derived from an env variable.

logging.info("Dev environment: Using MockMnsService")
return MockMnsService(MNS_TEST_QUEUE_URL)
else:
boto_config = Config(region_name="eu-west-2")
cache = Cache(directory="/tmp") # NOSONAR(S5443)
logging.info("Creating authenticator...")
authenticator = AppRestrictedAuth(
service=Service.PDS,
secret_manager_client=boto3.client("secretsmanager", config=boto_config),
environment=mns_env,
cache=cache,
)
logging.info("Authentication Initiated...")
return MnsService(authenticator)
36 changes: 36 additions & 0 deletions lambdas/shared/src/common/api_clients/mock_mns_service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import json
import os

import boto3

from common.clients import logger

REGION_NAME = os.getenv("AWS_REGION", "eu-west-2")


class MockMnsService:
def __init__(self, queue_url):
self.queue_url = queue_url
self.sqs_client = self._get_sqs_client()
logger.info(f"MockMnsService initialized with queue: {queue_url}")

def _get_sqs_client(self):
Comment thread
dlzhry2nhs marked this conversation as resolved.
Outdated
return boto3.client("sqs", region_name=REGION_NAME)

def publish_notification(self, mns_payload: dict) -> None:
Comment thread
dlzhry2nhs marked this conversation as resolved.
Outdated
"""
Send MNS notification payload to test SQS queue as fallback.
Args: payload: MNS notification payload
"""
try:
response = self.sqs_client.send_message(
QueueUrl=self.queue_url,
MessageBody=json.dumps(mns_payload),
MessageAttributes={"source": {"StringValue": "mns-publisher-lambda", "DataType": "String"}},
)
logger.info(
"Mock MNS: Successfully sent notification to test queue", extra={"message_id": response["MessageId"]}
)
except Exception:
logger.exception("Mock MNS: Failed to send to test SQS queue")
raise
Loading
Loading