diff --git a/.github/workflows/quality-checks.yml b/.github/workflows/quality-checks.yml index 8a8654ebf..038940f0e 100644 --- a/.github/workflows/quality-checks.yml +++ b/.github/workflows/quality-checks.yml @@ -174,6 +174,17 @@ jobs: poetry run coverage run --source=src -m unittest discover || echo "mesh_processor tests failed" >> ../../failed_tests.txt poetry run coverage xml -o ../../mesh_processor-coverage.xml + - name: Run unittest with mns_publisher + working-directory: lambdas/mns_publisher + id: mnspublisher + env: + PYTHONPATH: ${{ env.LAMBDA_PATH }}/mns_publisher/src:${{ env.LAMBDA_PATH }}/mns_publisher/tests:${{ env.SHARED_PATH }}/src + continue-on-error: true + run: | + poetry install + poetry run coverage run --source=src -m unittest discover || echo "mns_publisher tests failed" >> ../../failed_tests.txt + poetry run coverage xml -o ../../mns_publisher-coverage.xml + - name: Run unittest with coverage-mns-subscription working-directory: lambdas/mns_subscription id: mns_subscription diff --git a/infrastructure/instance/dynamodb.tf b/infrastructure/instance/dynamodb.tf index 22d60dcb7..9b382f012 100644 --- a/infrastructure/instance/dynamodb.tf +++ b/infrastructure/instance/dynamodb.tf @@ -56,6 +56,8 @@ resource "aws_dynamodb_table" "delta-dynamodb-table" { name = "imms-${local.resource_scope}-delta" billing_mode = "PAY_PER_REQUEST" hash_key = "PK" + stream_enabled = true + stream_view_type = "NEW_IMAGE" deletion_protection_enabled = !local.is_temp attribute { diff --git a/infrastructure/instance/mns_outbound_events_eb_pipe.tf b/infrastructure/instance/mns_outbound_events_eb_pipe.tf new file mode 100644 index 000000000..d68b282c1 --- /dev/null +++ b/infrastructure/instance/mns_outbound_events_eb_pipe.tf @@ -0,0 +1,118 @@ +# IAM Role for EventBridge Pipe +resource "aws_iam_role" "mns_outbound_events_eb_pipe" { + name = "${local.resource_scope}-mns-outbound-eventbridge-pipe-role" + assume_role_policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Action = "sts:AssumeRole" + Effect = "Allow" + Principal = { + Service = "pipes.amazonaws.com" + } + Condition = { + StringEquals = { + "aws:SourceAccount" = var.immunisation_account_id + } + } + } + ] + }) +} + +resource "aws_iam_role_policy" "mns_outbound_events_eb_pipe_source_policy" { + role = aws_iam_role.mns_outbound_events_eb_pipe.id + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + "Effect" : "Allow", + "Action" : [ + "dynamodb:DescribeStream", + "dynamodb:GetRecords", + "dynamodb:GetShardIterator", + "dynamodb:ListStreams" + ], + "Resource" : aws_dynamodb_table.delta-dynamodb-table.stream_arn + }, + { + "Effect" : "Allow", + "Action" : [ + "kms:Decrypt", + "kms:GenerateDataKey" + ], + "Resource" : data.aws_kms_key.existing_dynamo_encryption_key.arn + }, + ] + }) +} + +resource "aws_iam_role_policy" "mns_outbound_events_eb_pipe_target_policy" { + role = aws_iam_role.mns_outbound_events_eb_pipe.id + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Effect = "Allow" + Action = [ + "sqs:GetQueueAttributes", + "sqs:SendMessage", + ], + Resource = [ + aws_sqs_queue.mns_outbound_events.arn, + ] + }, + ] + }) +} + +resource "aws_iam_role_policy" "mns_outbound_events_eb_pipe_cw_log_policy" { + role = aws_iam_role.mns_outbound_events_eb_pipe.id + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Effect = "Allow" + Action = [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + Resource = [ + "arn:aws:logs:${var.aws_region}:${var.immunisation_account_id}:log-group:/aws/vendedlogs/pipes/${local.resource_scope}-mns-outbound-event-pipe-logs:*", + ] + }, + ] + }) +} + +resource "aws_cloudwatch_log_group" "mns_outbound_events_eb_pipe" { + name = "/aws/vendedlogs/pipes/${local.resource_scope}-mns-outbound-event-pipe-logs" + retention_in_days = 30 +} + +resource "aws_pipes_pipe" "mns_outbound_events" { + depends_on = [ + aws_iam_role_policy.mns_outbound_events_eb_pipe_source_policy, + aws_iam_role_policy.mns_outbound_events_eb_pipe_target_policy, + aws_iam_role_policy.mns_outbound_events_eb_pipe_cw_log_policy, + ] + name = "${local.resource_scope}-mns-outbound-events" + role_arn = aws_iam_role.mns_outbound_events_eb_pipe.arn + source = aws_dynamodb_table.delta-dynamodb-table.stream_arn + target = aws_sqs_queue.mns_outbound_events.arn + + source_parameters { + dynamodb_stream_parameters { + starting_position = "TRIM_HORIZON" + } + } + + log_configuration { + include_execution_data = ["ALL"] + level = "ERROR" + cloudwatch_logs_log_destination { + log_group_arn = aws_cloudwatch_log_group.pipe_log_group.arn + } + } +} diff --git a/infrastructure/instance/mns_publisher_lambda.tf b/infrastructure/instance/mns_publisher_lambda.tf new file mode 100644 index 000000000..ff803a0ae --- /dev/null +++ b/infrastructure/instance/mns_publisher_lambda.tf @@ -0,0 +1,238 @@ +locals { + mns_publisher_lambda_dir = abspath("${path.root}/../../lambdas/mns_publisher") + mns_publisher_lambda_files = fileset(local.mns_publisher_lambda_dir, "**") + mns_publisher_lambda_dir_sha = sha1(join("", [for f in local.mns_publisher_lambda_files : filesha1("${local.mns_publisher_lambda_dir}/${f}")])) + mns_publisher_lambda_name = "${local.short_prefix}-mns-publisher-lambda" +} + +resource "aws_ecr_repository" "mns_publisher_lambda_repository" { + image_scanning_configuration { + scan_on_push = true + } + name = "${local.short_prefix}-mns-publisher-repo" + force_delete = local.is_temp +} + +# Module for building and pushing Docker image to ECR +module "mns_publisher_docker_image" { + source = "terraform-aws-modules/lambda/aws//modules/docker-build" + version = "8.5.0" + docker_file_path = "./mns_publisher/Dockerfile" + + create_ecr_repo = false + ecr_repo = aws_ecr_repository.mns_publisher_lambda_repository.name + ecr_repo_lifecycle_policy = jsonencode({ + "rules" : [ + { + "rulePriority" : 1, + "description" : "Keep only the last 2 images", + "selection" : { + "tagStatus" : "any", + "countType" : "imageCountMoreThan", + "countNumber" : 2 + }, + "action" : { + "type" : "expire" + } + } + ] + }) + + platform = "linux/amd64" + use_image_tag = false + source_path = abspath("${path.root}/../../lambdas") + triggers = { + dir_sha = local.mns_publisher_lambda_dir_sha + shared_dir_sha = local.shared_dir_sha + } +} + +resource "aws_ecr_repository_policy" "mns_publisher_lambda_ecr_image_retrieval_policy" { + repository = aws_ecr_repository.mns_publisher_lambda_repository.name + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + "Sid" : "LambdaECRImageRetrievalPolicy", + "Effect" : "Allow", + "Principal" : { + "Service" : "lambda.amazonaws.com" + }, + "Action" : [ + "ecr:BatchGetImage", + "ecr:DeleteRepositoryPolicy", + "ecr:GetDownloadUrlForLayer", + "ecr:GetRepositoryPolicy", + "ecr:SetRepositoryPolicy" + ], + "Condition" : { + "StringLike" : { + "aws:sourceArn" : "arn:aws:lambda:${var.aws_region}:${var.immunisation_account_id}:function:${local.mns_publisher_lambda_name}" + } + } + } + ] + }) +} + +# IAM Role for Lambda +resource "aws_iam_role" "mns_publisher_lambda_exec_role" { + name = "${local.mns_publisher_lambda_name}-exec-role" + assume_role_policy = jsonencode({ + Version = "2012-10-17", + Statement = [{ + Effect = "Allow", + Sid = "", + Principal = { + Service = "lambda.amazonaws.com" + }, + Action = "sts:AssumeRole" + }] + }) +} + +# Policy for Lambda execution role +resource "aws_iam_policy" "mns_publisher_lambda_exec_policy" { + name = "${local.mns_publisher_lambda_name}-exec-policy" + policy = jsonencode({ + Version = "2012-10-17", + Statement = [ + { + Effect = "Allow" + Action = [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ] + Resource = "arn:aws:logs:${var.aws_region}:${var.immunisation_account_id}:log-group:/aws/lambda/${local.mns_publisher_lambda_name}:*" + }, + { + Effect = "Allow", + Action = [ + "ec2:CreateNetworkInterface", + "ec2:DescribeNetworkInterfaces", + "ec2:DeleteNetworkInterface" + ], + Resource = "*" + }, + { + "Effect" : "Allow", + "Action" : [ + "firehose:PutRecord", + "firehose:PutRecordBatch" + ], + "Resource" : "arn:aws:firehose:*:*:deliverystream/${module.splunk.firehose_stream_name}" + }, + { + Effect = "Allow", + Action = [ + "sqs:ReceiveMessage", + "sqs:DeleteMessage", + "sqs:GetQueueAttributes" + ], + Resource = aws_sqs_queue.mns_outbound_events.arn + } + ] + }) +} + +resource "aws_iam_policy" "mns_publisher_lambda_kms_access_policy" { + name = "${local.mns_publisher_lambda_name}-kms-policy" + description = "Allow Lambda to decrypt environment variables" + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Effect = "Allow" + Action = [ + "kms:Decrypt" + ] + Resource = data.aws_kms_key.existing_lambda_encryption_key.arn + } + ] + }) +} + +# Attach the execution policy to the Lambda role +resource "aws_iam_role_policy_attachment" "mns_publisher_lambda_exec_policy_attachment" { + role = aws_iam_role.mns_publisher_lambda_exec_role.name + policy_arn = aws_iam_policy.mns_publisher_lambda_exec_policy.arn +} + +# Attach the kms policy to the Lambda role +resource "aws_iam_role_policy_attachment" "mns_publisher_lambda_kms_policy_attachment" { + role = aws_iam_role.mns_publisher_lambda_exec_role.name + policy_arn = aws_iam_policy.mns_publisher_lambda_kms_access_policy.arn +} + +# Lambda Function with Security Group and VPC. +resource "aws_lambda_function" "mns_publisher_lambda" { + function_name = local.mns_publisher_lambda_name + role = aws_iam_role.mns_publisher_lambda_exec_role.arn + package_type = "Image" + image_uri = module.mns_publisher_docker_image.image_uri + architectures = ["x86_64"] + timeout = 120 + + vpc_config { + subnet_ids = local.private_subnet_ids + security_group_ids = [data.aws_security_group.existing_securitygroup.id] + } + + environment { + variables = { + SPLUNK_FIREHOSE_NAME = module.splunk.firehose_stream_name + } + } + + kms_key_arn = data.aws_kms_key.existing_lambda_encryption_key.arn + reserved_concurrent_executions = local.is_temp ? -1 : 20 + depends_on = [ + aws_cloudwatch_log_group.mns_publisher_lambda_log_group, + aws_iam_policy.mns_publisher_lambda_exec_policy + ] +} + +resource "aws_cloudwatch_log_group" "mns_publisher_lambda_log_group" { + name = "/aws/lambda/${local.mns_publisher_lambda_name}" + retention_in_days = 30 +} + +resource "aws_lambda_event_source_mapping" "mns_outbound_event_sqs_to_lambda" { + event_source_arn = aws_sqs_queue.mns_outbound_events.arn + function_name = aws_lambda_function.mns_publisher_lambda.arn + batch_size = 10 + enabled = true +} + +resource "aws_cloudwatch_log_metric_filter" "mns_publisher_error_logs" { + count = var.error_alarm_notifications_enabled ? 1 : 0 + + name = "${local.short_prefix}-MnsPublisherErrorLogsFilter" + pattern = "%\\[ERROR\\]%" + log_group_name = aws_cloudwatch_log_group.mns_publisher_lambda_log_group.name + + metric_transformation { + name = "${local.short_prefix}-MnsPublisherErrorLogs" + namespace = "${local.short_prefix}-MnsPublisherLambda" + value = "1" + } +} + +resource "aws_cloudwatch_metric_alarm" "mns_publisher_error_alarm" { + count = var.error_alarm_notifications_enabled ? 1 : 0 + + alarm_name = "${local.mns_publisher_lambda_name}-error" + comparison_operator = "GreaterThanOrEqualToThreshold" + evaluation_periods = 1 + metric_name = "${local.short_prefix}-MnsPublisherErrorLogs" + namespace = "${local.short_prefix}-MnsPublisherLambda" + period = 120 + statistic = "Sum" + threshold = 1 + alarm_description = "This sets off an alarm for any error logs found in the MNS Publisher Lambda function" + alarm_actions = [data.aws_sns_topic.imms_system_alert_errors.arn] + treat_missing_data = "notBreaching" +} diff --git a/infrastructure/instance/sqs_mns_outbound_events.tf b/infrastructure/instance/sqs_mns_outbound_events.tf new file mode 100644 index 000000000..039ca0084 --- /dev/null +++ b/infrastructure/instance/sqs_mns_outbound_events.tf @@ -0,0 +1,33 @@ +resource "aws_sqs_queue" "mns_outbound_events" { + name = "${local.resource_scope}-mns-outbound-events" + fifo_queue = false + visibility_timeout_seconds = 180 +} + +data "aws_iam_policy_document" "mns_outbound_events_sqs_policy" { + statement { + sid = "mns-outbound-allow-eb-pipe-access" + effect = "Allow" + + principals { + type = "AWS" + identifiers = [aws_iam_role.mns_outbound_events_eb_pipe.arn] + } + + actions = [ + "sqs:SendMessage", + ] + + resources = [ + aws_sqs_queue.mns_outbound_events.arn + ] + } +} + +resource "aws_sqs_queue_policy" "mns_outbound_events_sqs" { + queue_url = aws_sqs_queue.mns_outbound_events.id + policy = data.aws_iam_policy_document.mns_outbound_events_sqs_policy.json +} + +# TODO - (follow on once we have basics set up so Lambda coding can start) +# Add KMS encryption to queue, add DLQ and redrive diff --git a/lambdas/mns_publisher/Dockerfile b/lambdas/mns_publisher/Dockerfile new file mode 100644 index 000000000..ade0dbb58 --- /dev/null +++ b/lambdas/mns_publisher/Dockerfile @@ -0,0 +1,34 @@ +FROM public.ecr.aws/lambda/python:3.11 AS base + +RUN mkdir -p /home/appuser && \ + echo 'appuser:x:1001:1001::/home/appuser:/sbin/nologin' >> /etc/passwd && \ + echo 'appuser:x:1001:' >> /etc/group && \ + chown -R 1001:1001 /home/appuser && pip install "poetry~=2.1.4" + +# Copy mns_publisher Poetry files +COPY ./mns_publisher/poetry.lock ./mns_publisher/pyproject.toml ./ + +# Install mns_publisher dependencies +WORKDIR /var/task +RUN poetry config virtualenvs.create false && poetry install --no-interaction --no-ansi --no-root --only main + +# ----------------------------- +FROM base AS build + +# Set working directory back to Lambda task root +WORKDIR /var/task + +# Copy shared source code +COPY ./shared/src/common ./common + +# Copy mns_publisher source code +COPY ./mns_publisher/src . + +# Set correct permissions +RUN chmod 644 $(find . -type f) && chmod 755 $(find . -type d) + +# Switch to the non-root user for running the container +USER 1001:1001 + +# Set the Lambda handler +CMD ["lambda_handler.lambda_handler"] diff --git a/lambdas/mns_publisher/Makefile b/lambdas/mns_publisher/Makefile new file mode 100644 index 000000000..d50de37aa --- /dev/null +++ b/lambdas/mns_publisher/Makefile @@ -0,0 +1,22 @@ +TEST_ENV := @PYTHONPATH=src:tests:../shared/src + +build: + docker build -t mnspublisher-lambda-build -f Dockerfile . + +package: build + mkdir -p build + docker run --rm -v $(shell pwd)/build:/build mnspublisher-lambda-build + +test: + $(TEST_ENV) python -m unittest + +coverage-run: + $(TEST_ENV) coverage run --source=src -m unittest discover + +coverage-report: + $(TEST_ENV) coverage report -m + +coverage-html: + $(TEST_ENV) coverage html + +.PHONY: build package test diff --git a/lambdas/mns_publisher/README.md b/lambdas/mns_publisher/README.md new file mode 100644 index 000000000..dea5a78fa --- /dev/null +++ b/lambdas/mns_publisher/README.md @@ -0,0 +1,3 @@ +# mns-publisher + +Add description - TODO diff --git a/lambdas/mns_publisher/poetry.lock b/lambdas/mns_publisher/poetry.lock new file mode 100644 index 000000000..dd85f0924 --- /dev/null +++ b/lambdas/mns_publisher/poetry.lock @@ -0,0 +1,137 @@ +# This file is automatically @generated by Poetry 2.1.4 and should not be changed by hand. + +[[package]] +name = "aws-lambda-typing" +version = "2.20.0" +description = "A package that provides type hints for AWS Lambda event, context and response objects" +optional = false +python-versions = "<4.0,>=3.6" +groups = ["main"] +files = [ + {file = "aws-lambda-typing-2.20.0.tar.gz", hash = "sha256:78b0d8ebab73b3a6b0da98a7969f4e9c4bb497298ec50f3217da8a8dfba17154"}, + {file = "aws_lambda_typing-2.20.0-py3-none-any.whl", hash = "sha256:1d44264cabfeab5ac38e67ddd0c874e677b2cbbae77a42d0519df470e6bbb49b"}, +] + +[[package]] +name = "coverage" +version = "7.13.4" +description = "Code coverage measurement for Python" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "coverage-7.13.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0fc31c787a84f8cd6027eba44010517020e0d18487064cd3d8968941856d1415"}, + {file = "coverage-7.13.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a32ebc02a1805adf637fc8dec324b5cdacd2e493515424f70ee33799573d661b"}, + {file = "coverage-7.13.4-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e24f9156097ff9dc286f2f913df3a7f63c0e333dcafa3c196f2c18b4175ca09a"}, + {file = "coverage-7.13.4-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8041b6c5bfdc03257666e9881d33b1abc88daccaf73f7b6340fb7946655cd10f"}, + {file = "coverage-7.13.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2a09cfa6a5862bc2fc6ca7c3def5b2926194a56b8ab78ffcf617d28911123012"}, + {file = "coverage-7.13.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:296f8b0af861d3970c2a4d8c91d48eb4dd4771bcef9baedec6a9b515d7de3def"}, + {file = "coverage-7.13.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e101609bcbbfb04605ea1027b10dc3735c094d12d40826a60f897b98b1c30256"}, + {file = "coverage-7.13.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:aa3feb8db2e87ff5e6d00d7e1480ae241876286691265657b500886c98f38bda"}, + {file = "coverage-7.13.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:4fc7fa81bbaf5a02801b65346c8b3e657f1d93763e58c0abdf7c992addd81a92"}, + {file = "coverage-7.13.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:33901f604424145c6e9c2398684b92e176c0b12df77d52db81c20abd48c3794c"}, + {file = "coverage-7.13.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:bb28c0f2cf2782508a40cec377935829d5fcc3ad9a3681375af4e84eb34b6b58"}, + {file = "coverage-7.13.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9d107aff57a83222ddbd8d9ee705ede2af2cc926608b57abed8ef96b50b7e8f9"}, + {file = "coverage-7.13.4-cp310-cp310-win32.whl", hash = "sha256:a6f94a7d00eb18f1b6d403c91a88fd58cfc92d4b16080dfdb774afc8294469bf"}, + {file = "coverage-7.13.4-cp310-cp310-win_amd64.whl", hash = "sha256:2cb0f1e000ebc419632bbe04366a8990b6e32c4e0b51543a6484ffe15eaeda95"}, + {file = "coverage-7.13.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d490ba50c3f35dd7c17953c68f3270e7ccd1c6642e2d2afe2d8e720b98f5a053"}, + {file = "coverage-7.13.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:19bc3c88078789f8ef36acb014d7241961dbf883fd2533d18cb1e7a5b4e28b11"}, + {file = "coverage-7.13.4-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3998e5a32e62fdf410c0dbd3115df86297995d6e3429af80b8798aad894ca7aa"}, + {file = "coverage-7.13.4-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8e264226ec98e01a8e1054314af91ee6cde0eacac4f465cc93b03dbe0bce2fd7"}, + {file = "coverage-7.13.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a3aa4e7b9e416774b21797365b358a6e827ffadaaca81b69ee02946852449f00"}, + {file = "coverage-7.13.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:71ca20079dd8f27fcf808817e281e90220475cd75115162218d0e27549f95fef"}, + {file = "coverage-7.13.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e2f25215f1a359ab17320b47bcdaca3e6e6356652e8256f2441e4ef972052903"}, + {file = "coverage-7.13.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d65b2d373032411e86960604dc4edac91fdfb5dca539461cf2cbe78327d1e64f"}, + {file = "coverage-7.13.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94eb63f9b363180aff17de3e7c8760c3ba94664ea2695c52f10111244d16a299"}, + {file = "coverage-7.13.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e856bf6616714c3a9fbc270ab54103f4e685ba236fa98c054e8f87f266c93505"}, + {file = "coverage-7.13.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:65dfcbe305c3dfe658492df2d85259e0d79ead4177f9ae724b6fb245198f55d6"}, + {file = "coverage-7.13.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b507778ae8a4c915436ed5c2e05b4a6cecfa70f734e19c22a005152a11c7b6a9"}, + {file = "coverage-7.13.4-cp311-cp311-win32.whl", hash = "sha256:784fc3cf8be001197b652d51d3fd259b1e2262888693a4636e18879f613a62a9"}, + {file = "coverage-7.13.4-cp311-cp311-win_amd64.whl", hash = "sha256:2421d591f8ca05b308cf0092807308b2facbefe54af7c02ac22548b88b95c98f"}, + {file = "coverage-7.13.4-cp311-cp311-win_arm64.whl", hash = "sha256:79e73a76b854d9c6088fe5d8b2ebe745f8681c55f7397c3c0a016192d681045f"}, + {file = "coverage-7.13.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:02231499b08dabbe2b96612993e5fc34217cdae907a51b906ac7fca8027a4459"}, + {file = "coverage-7.13.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40aa8808140e55dc022b15d8aa7f651b6b3d68b365ea0398f1441e0b04d859c3"}, + {file = "coverage-7.13.4-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5b856a8ccf749480024ff3bd7310adaef57bf31fd17e1bfc404b7940b6986634"}, + {file = "coverage-7.13.4-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2c048ea43875fbf8b45d476ad79f179809c590ec7b79e2035c662e7afa3192e3"}, + {file = "coverage-7.13.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b7b38448866e83176e28086674fe7368ab8590e4610fb662b44e345b86d63ffa"}, + {file = "coverage-7.13.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:de6defc1c9badbf8b9e67ae90fd00519186d6ab64e5cc5f3d21359c2a9b2c1d3"}, + {file = "coverage-7.13.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7eda778067ad7ffccd23ecffce537dface96212576a07924cbf0d8799d2ded5a"}, + {file = "coverage-7.13.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e87f6c587c3f34356c3759f0420693e35e7eb0e2e41e4c011cb6ec6ecbbf1db7"}, + {file = "coverage-7.13.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:8248977c2e33aecb2ced42fef99f2d319e9904a36e55a8a68b69207fb7e43edc"}, + {file = "coverage-7.13.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:25381386e80ae727608e662474db537d4df1ecd42379b5ba33c84633a2b36d47"}, + {file = "coverage-7.13.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:ee756f00726693e5ba94d6df2bdfd64d4852d23b09bb0bc700e3b30e6f333985"}, + {file = "coverage-7.13.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fdfc1e28e7c7cdce44985b3043bc13bbd9c747520f94a4d7164af8260b3d91f0"}, + {file = "coverage-7.13.4-cp312-cp312-win32.whl", hash = "sha256:01d4cbc3c283a17fc1e42d614a119f7f438eabb593391283adca8dc86eff1246"}, + {file = "coverage-7.13.4-cp312-cp312-win_amd64.whl", hash = "sha256:9401ebc7ef522f01d01d45532c68c5ac40fb27113019b6b7d8b208f6e9baa126"}, + {file = "coverage-7.13.4-cp312-cp312-win_arm64.whl", hash = "sha256:b1ec7b6b6e93255f952e27ab58fbc68dcc468844b16ecbee881aeb29b6ab4d8d"}, + {file = "coverage-7.13.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b66a2da594b6068b48b2692f043f35d4d3693fb639d5ea8b39533c2ad9ac3ab9"}, + {file = "coverage-7.13.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3599eb3992d814d23b35c536c28df1a882caa950f8f507cef23d1cbf334995ac"}, + {file = "coverage-7.13.4-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:93550784d9281e374fb5a12bf1324cc8a963fd63b2d2f223503ef0fd4aa339ea"}, + {file = "coverage-7.13.4-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b720ce6a88a2755f7c697c23268ddc47a571b88052e6b155224347389fdf6a3b"}, + {file = "coverage-7.13.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7b322db1284a2ed3aa28ffd8ebe3db91c929b7a333c0820abec3d838ef5b3525"}, + {file = "coverage-7.13.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f4594c67d8a7c89cf922d9df0438c7c7bb022ad506eddb0fdb2863359ff78242"}, + {file = "coverage-7.13.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:53d133df809c743eb8bce33b24bcababb371f4441340578cd406e084d94a6148"}, + {file = "coverage-7.13.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:76451d1978b95ba6507a039090ba076105c87cc76fc3efd5d35d72093964d49a"}, + {file = "coverage-7.13.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7f57b33491e281e962021de110b451ab8a24182589be17e12a22c79047935e23"}, + {file = "coverage-7.13.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:1731dc33dc276dafc410a885cbf5992f1ff171393e48a21453b78727d090de80"}, + {file = "coverage-7.13.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:bd60d4fe2f6fa7dff9223ca1bbc9f05d2b6697bc5961072e5d3b952d46e1b1ea"}, + {file = "coverage-7.13.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9181a3ccead280b828fae232df12b16652702b49d41e99d657f46cc7b1f6ec7a"}, + {file = "coverage-7.13.4-cp313-cp313-win32.whl", hash = "sha256:f53d492307962561ac7de4cd1de3e363589b000ab69617c6156a16ba7237998d"}, + {file = "coverage-7.13.4-cp313-cp313-win_amd64.whl", hash = "sha256:e6f70dec1cc557e52df5306d051ef56003f74d56e9c4dd7ddb07e07ef32a84dd"}, + {file = "coverage-7.13.4-cp313-cp313-win_arm64.whl", hash = "sha256:fb07dc5da7e849e2ad31a5d74e9bece81f30ecf5a42909d0a695f8bd1874d6af"}, + {file = "coverage-7.13.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:40d74da8e6c4b9ac18b15331c4b5ebc35a17069410cad462ad4f40dcd2d50c0d"}, + {file = "coverage-7.13.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4223b4230a376138939a9173f1bdd6521994f2aff8047fae100d6d94d50c5a12"}, + {file = "coverage-7.13.4-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1d4be36a5114c499f9f1f9195e95ebf979460dbe2d88e6816ea202010ba1c34b"}, + {file = "coverage-7.13.4-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:200dea7d1e8095cc6e98cdabe3fd1d21ab17d3cee6dab00cadbb2fe35d9c15b9"}, + {file = "coverage-7.13.4-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b8eb931ee8e6d8243e253e5ed7336deea6904369d2fd8ae6e43f68abbf167092"}, + {file = "coverage-7.13.4-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:75eab1ebe4f2f64d9509b984f9314d4aa788540368218b858dad56dc8f3e5eb9"}, + {file = "coverage-7.13.4-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c35eb28c1d085eb7d8c9b3296567a1bebe03ce72962e932431b9a61f28facf26"}, + {file = "coverage-7.13.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:eb88b316ec33760714a4720feb2816a3a59180fd58c1985012054fa7aebee4c2"}, + {file = "coverage-7.13.4-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:7d41eead3cc673cbd38a4417deb7fd0b4ca26954ff7dc6078e33f6ff97bed940"}, + {file = "coverage-7.13.4-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:fb26a934946a6afe0e326aebe0730cdff393a8bc0bbb65a2f41e30feddca399c"}, + {file = "coverage-7.13.4-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:dae88bc0fc77edaa65c14be099bd57ee140cf507e6bfdeea7938457ab387efb0"}, + {file = "coverage-7.13.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:845f352911777a8e722bfce168958214951e07e47e5d5d9744109fa5fe77f79b"}, + {file = "coverage-7.13.4-cp313-cp313t-win32.whl", hash = "sha256:2fa8d5f8de70688a28240de9e139fa16b153cc3cbb01c5f16d88d6505ebdadf9"}, + {file = "coverage-7.13.4-cp313-cp313t-win_amd64.whl", hash = "sha256:9351229c8c8407645840edcc277f4a2d44814d1bc34a2128c11c2a031d45a5dd"}, + {file = "coverage-7.13.4-cp313-cp313t-win_arm64.whl", hash = "sha256:30b8d0512f2dc8c8747557e8fb459d6176a2c9e5731e2b74d311c03b78451997"}, + {file = "coverage-7.13.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:300deaee342f90696ed186e3a00c71b5b3d27bffe9e827677954f4ee56969601"}, + {file = "coverage-7.13.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:29e3220258d682b6226a9b0925bc563ed9a1ebcff3cad30f043eceea7eaf2689"}, + {file = "coverage-7.13.4-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:391ee8f19bef69210978363ca930f7328081c6a0152f1166c91f0b5fdd2a773c"}, + {file = "coverage-7.13.4-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0dd7ab8278f0d58a0128ba2fca25824321f05d059c1441800e934ff2efa52129"}, + {file = "coverage-7.13.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:78cdf0d578b15148b009ccf18c686aa4f719d887e76e6b40c38ffb61d264a552"}, + {file = "coverage-7.13.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:48685fee12c2eb3b27c62f2658e7ea21e9c3239cba5a8a242801a0a3f6a8c62a"}, + {file = "coverage-7.13.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4e83efc079eb39480e6346a15a1bcb3e9b04759c5202d157e1dd4303cd619356"}, + {file = "coverage-7.13.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ecae9737b72408d6a950f7e525f30aca12d4bd8dd95e37342e5beb3a2a8c4f71"}, + {file = "coverage-7.13.4-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ae4578f8528569d3cf303fef2ea569c7f4c4059a38c8667ccef15c6e1f118aa5"}, + {file = "coverage-7.13.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:6fdef321fdfbb30a197efa02d48fcd9981f0d8ad2ae8903ac318adc653f5df98"}, + {file = "coverage-7.13.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b0f6ccf3dbe577170bebfce1318707d0e8c3650003cb4b3a9dd744575daa8b5"}, + {file = "coverage-7.13.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:75fcd519f2a5765db3f0e391eb3b7d150cce1a771bf4c9f861aeab86c767a3c0"}, + {file = "coverage-7.13.4-cp314-cp314-win32.whl", hash = "sha256:8e798c266c378da2bd819b0677df41ab46d78065fb2a399558f3f6cae78b2fbb"}, + {file = "coverage-7.13.4-cp314-cp314-win_amd64.whl", hash = "sha256:245e37f664d89861cf2329c9afa2c1fe9e6d4e1a09d872c947e70718aeeac505"}, + {file = "coverage-7.13.4-cp314-cp314-win_arm64.whl", hash = "sha256:ad27098a189e5838900ce4c2a99f2fe42a0bf0c2093c17c69b45a71579e8d4a2"}, + {file = "coverage-7.13.4-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:85480adfb35ffc32d40918aad81b89c69c9cc5661a9b8a81476d3e645321a056"}, + {file = "coverage-7.13.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:79be69cf7f3bf9b0deeeb062eab7ac7f36cd4cc4c4dd694bd28921ba4d8596cc"}, + {file = "coverage-7.13.4-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:caa421e2684e382c5d8973ac55e4f36bed6821a9bad5c953494de960c74595c9"}, + {file = "coverage-7.13.4-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:14375934243ee05f56c45393fe2ce81fe5cc503c07cee2bdf1725fb8bef3ffaf"}, + {file = "coverage-7.13.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:25a41c3104d08edb094d9db0d905ca54d0cd41c928bb6be3c4c799a54753af55"}, + {file = "coverage-7.13.4-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6f01afcff62bf9a08fb32b2c1d6e924236c0383c02c790732b6537269e466a72"}, + {file = "coverage-7.13.4-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:eb9078108fbf0bcdde37c3f4779303673c2fa1fe8f7956e68d447d0dd426d38a"}, + {file = "coverage-7.13.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0e086334e8537ddd17e5f16a344777c1ab8194986ec533711cbe6c41cde841b6"}, + {file = "coverage-7.13.4-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:725d985c5ab621268b2edb8e50dfe57633dc69bda071abc470fed55a14935fd3"}, + {file = "coverage-7.13.4-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:3c06f0f1337c667b971ca2f975523347e63ec5e500b9aa5882d91931cd3ef750"}, + {file = "coverage-7.13.4-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:590c0ed4bf8e85f745e6b805b2e1c457b2e33d5255dd9729743165253bc9ad39"}, + {file = "coverage-7.13.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:eb30bf180de3f632cd043322dad5751390e5385108b2807368997d1a92a509d0"}, + {file = "coverage-7.13.4-cp314-cp314t-win32.whl", hash = "sha256:c4240e7eded42d131a2d2c4dec70374b781b043ddc79a9de4d55ca71f8e98aea"}, + {file = "coverage-7.13.4-cp314-cp314t-win_amd64.whl", hash = "sha256:4c7d3cc01e7350f2f0f6f7036caaf5673fb56b6998889ccfe9e1c1fe75a9c932"}, + {file = "coverage-7.13.4-cp314-cp314t-win_arm64.whl", hash = "sha256:23e3f687cf945070d1c90f85db66d11e3025665d8dafa831301a0e0038f3db9b"}, + {file = "coverage-7.13.4-py3-none-any.whl", hash = "sha256:1af1641e57cf7ba1bd67d677c9abdbcd6cc2ab7da3bca7fa1e2b7e50e65f2ad0"}, + {file = "coverage-7.13.4.tar.gz", hash = "sha256:e5c8f6ed1e61a8b2dcdf31eb0b9bbf0130750ca79c1c49eb898e2ad86f5ccc91"}, +] + +[package.extras] +toml = ["tomli ; python_full_version <= \"3.11.0a6\""] + +[metadata] +lock-version = "2.1" +python-versions = "~3.11" +content-hash = "0a2b2f2ca62bb0da43789e3a2c1c8c943545f1461b988de1c012d9de64cae545" diff --git a/lambdas/mns_publisher/pyproject.toml b/lambdas/mns_publisher/pyproject.toml new file mode 100644 index 000000000..009b8bc9a --- /dev/null +++ b/lambdas/mns_publisher/pyproject.toml @@ -0,0 +1,19 @@ +[tool.poetry] +name = "mns_publisher" +version = "1.0.0" +description = "Lambda function to publish immunisation events to MNS" +authors = ["VED Team "] +readme = "README.md" +packages = [ + {include = "src"}, + {include = "common", from = "../shared/src"} +] + +[tool.poetry.dependencies] +python = "~3.11" +aws-lambda-typing = "~2.20.0" +coverage = "^7.13.2" + +[build-system] +requires = ["poetry-core >= 1.5.0"] +build-backend = "poetry.core.masonry.api" diff --git a/lambdas/mns_publisher/src/__init__.py b/lambdas/mns_publisher/src/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/lambdas/mns_publisher/src/lambda_handler.py b/lambdas/mns_publisher/src/lambda_handler.py new file mode 100644 index 000000000..0dbec6681 --- /dev/null +++ b/lambdas/mns_publisher/src/lambda_handler.py @@ -0,0 +1,10 @@ +from aws_lambda_typing import context, events + + +def lambda_handler(event: events.SQSEvent, _: context.Context) -> bool: + event_records = event.get("Records", []) + + for record in event_records: + print(record) + + return True diff --git a/lambdas/mns_publisher/tests/__init__.py b/lambdas/mns_publisher/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/lambdas/mns_publisher/tests/test_lambda_handler.py b/lambdas/mns_publisher/tests/test_lambda_handler.py new file mode 100644 index 000000000..d6409ff2a --- /dev/null +++ b/lambdas/mns_publisher/tests/test_lambda_handler.py @@ -0,0 +1,9 @@ +from unittest import TestCase +from unittest.mock import Mock + +from lambda_handler import lambda_handler + + +class TestLambdaHandler(TestCase): + def test_lambda_handler_returns_true(self): + lambda_handler({"Records": [{"messageId": "1234"}]}, Mock()) diff --git a/sonar-project.properties b/sonar-project.properties index 0c41e3327..4e62f4420 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -5,7 +5,7 @@ sonar.host.url=https://sonarcloud.io sonar.python.version=3.11 sonar.exclusions=**/proxies/**,**/utilities/scripts/**,**/infrastructure/account/**,**/infrastructure/instance/**,**/infrastructure/grafana/**,**/terraform_aws_backup/**,**/tests/** sonar.coverage.exclusions=lambdas/shared/src/common/models/batch_constants.py -sonar.python.coverage.reportPaths=backend-coverage.xml,delta-coverage.xml,ack-lambda-coverage.xml,filenameprocessor-coverage.xml,recordforwarder-coverage.xml,recordprocessor-coverage.xml,mesh_processor-coverage.xml,redis_sync-coverage.xml,mns_subscription-coverage.xml,id_sync-coverage.xml,shared-coverage.xml,batchprocessorfilter-coverage.xml +sonar.python.coverage.reportPaths=backend-coverage.xml,delta-coverage.xml,ack-lambda-coverage.xml,filenameprocessor-coverage.xml,recordforwarder-coverage.xml,recordprocessor-coverage.xml,mesh_processor-coverage.xml,redis_sync-coverage.xml,mns_subscription-coverage.xml,id_sync-coverage.xml,shared-coverage.xml,batchprocessorfilter-coverage.xml,mns_publisher-coverage.xml sonar.cpd.exclusions=**/Dockerfile sonar.issue.ignore.multicriteria=exclude_snomed_urls,exclude_hl7_urls sonar.issue.ignore.multicriteria.exclude_snomed_urls.ruleKey=python:S5332