Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release/2024-11-20 #419

Closed
wants to merge 13 commits into from
Closed
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
2 changes: 1 addition & 1 deletion .github/workflows/deploy-nonprod-workspace.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ on:
options:
- dev
- qa
- int
# - int
- ref
sandbox:
description: Do you want to deploy the sandbox version?
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pull-requests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ jobs:
ref: ${{ env.BRANCH_NAME }}
- uses: ./.github/actions/make/
with:
command: test--feature--integration
command: test--feature--integration PYTEST_FLAGS=-xvv
requires-aws: true

test--smoke:
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## 2024-11-20
- [PI-528] Collapse versioning to v1
- [PI-581] MHS Device with Device Reference Data

## 2024-11-19
- [PI-601] Workspace destroy, use main branch as fallback

Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2024.11.19
2024.11.20
2 changes: 2 additions & 0 deletions changelog/2024-11-20.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- [PI-528] Collapse versioning to v1
- [PI-581] MHS Device with Device Reference Data
3 changes: 2 additions & 1 deletion infrastructure/terraform/per_workspace/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ module "lambdas" {
source = "./modules/api_worker/api_lambda"
python_version = var.python_version
name = each.key
lambda_name = "${local.project}--${replace(terraform.workspace, "_", "-")}--${replace(replace(replace(each.key, "_", "-"), "DeviceReferenceData", "DeviceRefData"), "MessageHandlingSystem", "MHS")}"
lambda_name = "${local.project}--${replace(terraform.workspace, "_", "-")}--${replace(replace(replace(replace(each.key, "_", "-"), "DeviceReferenceData", "DeviceRefData"), "MessageHandlingSystem", "MHS"), "MessageSet", "MsgSet")}"

//Compact will remove all nulls from the list and create a new one - this is because TF throws an error if there is a null item in the list.
layers = concat(
compact([for instance in module.layers : contains(var.api_lambda_layers, instance.name) ? instance.layer_arn : null]),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ locals {
}
methods = [
for lambda_alias in setsubtract(var.lambdas, ["authoriser"]) :
{ "method_${lambda_alias}" = "${local.apigateway_lambda_arn_prefix}:${var.assume_account}:function:${var.project}--${replace(terraform.workspace, "_", "-")}--${replace(replace(replace(lambda_alias, "_", "-"), "DeviceReferenceData", "DeviceRefData"), "MessageHandlingSystem", "MHS")}/invocations" }
{ "method_${lambda_alias}" = "${local.apigateway_lambda_arn_prefix}:${var.assume_account}:function:${var.project}--${replace(terraform.workspace, "_", "-")}--${replace(replace(replace(replace(lambda_alias, "_", "-"), "DeviceReferenceData", "DeviceRefData"), "MessageHandlingSystem", "MHS"), "MessageSet", "MsgSet")}/invocations" }
]
swagger_file = templatefile("${path.root}/../../swagger/dist/aws/swagger.yaml", merge({
lambda_invoke_arn = var.authoriser_metadata.lambda_invoke_arn,
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "connecting-party-manager"
version = "2024.11.19"
version = "2024.11.20"
description = "Repository for the Connecting Party Manager API and related services"
authors = ["NHS England"]
license = "LICENSE.md"
Expand Down
4 changes: 2 additions & 2 deletions src/api/createCpmProduct/tests/test_index.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
from unittest import mock

import pytest
from domain.core.root.v3 import Root
from domain.repository.product_team_repository.v2 import ProductTeamRepository
from domain.core.root import Root
from domain.repository.product_team_repository import ProductTeamRepository
from event.json import json_loads

from test_helpers.dynamodb import mock_table
Expand Down
6 changes: 3 additions & 3 deletions src/api/createCpmProductForEpr/src/v1/steps.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
read_product_team,
)
from domain.core.cpm_product import CpmProduct
from domain.core.cpm_system_id.v1 import PartyKeyId
from domain.core.product_key.v1 import ProductKeyType
from domain.core.product_team.v3 import ProductTeam
from domain.core.cpm_system_id import PartyKeyId
from domain.core.product_key import ProductKeyType
from domain.core.product_team import ProductTeam
from domain.repository.cpm_system_id_repository import CpmSystemIdRepository


Expand Down
8 changes: 4 additions & 4 deletions src/api/createCpmProductForEpr/tests/test_index.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
from unittest import mock

import pytest
from domain.core.cpm_system_id.v1 import PartyKeyId, ProductId
from domain.core.cpm_system_id import PartyKeyId, ProductId
from domain.core.enum import Status
from domain.core.root.v3 import Root
from domain.repository.cpm_product_repository.v3 import CpmProductRepository
from domain.repository.product_team_repository.v2 import ProductTeamRepository
from domain.core.root import Root
from domain.repository.cpm_product_repository import CpmProductRepository
from domain.repository.product_team_repository import ProductTeamRepository
from event.json import json_loads

from test_helpers.dynamodb import mock_table
Expand Down
10 changes: 5 additions & 5 deletions src/api/createDevice/src/v1/steps.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@
read_product,
read_product_team,
)
from domain.core.cpm_product.v1 import CpmProduct
from domain.core.device.v3 import Device
from domain.repository.device_repository.v3 import DeviceRepository
from domain.request_models.v1 import CreateDeviceIncomingParams
from domain.core.cpm_product import CpmProduct
from domain.core.device import Device
from domain.repository.device_repository import DeviceRepository
from domain.request_models import CreateDeviceIncomingParams
from domain.response.validation_errors import mark_validation_errors_as_inbound


@mark_validation_errors_as_inbound
def parse_device_payload(data, cache) -> Device:
def parse_device_payload(data, cache) -> CreateDeviceIncomingParams:
payload: dict = data[parse_event_body]
return CreateDeviceIncomingParams(**payload)

Expand Down
14 changes: 7 additions & 7 deletions src/api/createDevice/tests/test_index.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@
from unittest import mock

import pytest
from domain.core.cpm_product.v1 import CpmProduct
from domain.core.cpm_system_id.v1 import ProductId
from domain.core.device.v3 import Device
from domain.core.root.v3 import Root
from domain.repository.cpm_product_repository.v3 import CpmProductRepository
from domain.repository.device_repository.v3 import DeviceRepository
from domain.repository.product_team_repository.v2 import ProductTeamRepository
from domain.core.cpm_product import CpmProduct
from domain.core.cpm_system_id import ProductId
from domain.core.device import Device
from domain.core.root import Root
from domain.repository.cpm_product_repository import CpmProductRepository
from domain.repository.device_repository import DeviceRepository
from domain.repository.product_team_repository import ProductTeamRepository
from event.json import json_loads

from test_helpers.dynamodb import mock_table
Expand Down
144 changes: 110 additions & 34 deletions src/api/createDeviceMessageHandlingSystem/src/v1/steps.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,51 +7,92 @@
read_product,
read_product_team,
)
from domain.core.cpm_product.v1 import CpmProduct
from domain.core.device.v3 import Device
from domain.core.error import InvalidSpineMhsResponse
from domain.core.questionnaire.v3 import Questionnaire, QuestionnaireResponse
from domain.repository.device_repository.v3 import DeviceRepository
from domain.repository.questionnaire_repository.v2 import QuestionnaireRepository
from domain.repository.questionnaire_repository.v2.questionnaires import (
from domain.core.cpm_product import CpmProduct
from domain.core.device import (
MHS_DEVICE_NAME,
Device,
DeviceKeyAddedEvent,
DeviceReferenceDataIdAddedEvent,
DeviceTagAddedEvent,
QuestionnaireResponseUpdatedEvent,
)
from domain.core.device_key import DeviceKeyType
from domain.core.device_reference_data import DeviceReferenceData
from domain.core.error import ConfigurationError
from domain.core.product_team import ProductTeam
from domain.core.questionnaire import Questionnaire, QuestionnaireResponse
from domain.repository.device_reference_data_repository import (
DeviceReferenceDataRepository,
)
from domain.repository.device_repository import DeviceRepository
from domain.repository.questionnaire_repository import (
QuestionnaireInstance,
QuestionnaireRepository,
)
from domain.request_models.v1 import CreateMhsDeviceIncomingParams
from domain.request_models import CpmProductPathParams, CreateMhsDeviceIncomingParams
from domain.response.validation_errors import mark_validation_errors_as_inbound


@mark_validation_errors_as_inbound
def parse_mhs_device_payload(data, cache) -> Device:
def parse_mhs_device_payload(data, cache) -> CreateMhsDeviceIncomingParams:
payload: dict = data[parse_event_body]
return CreateMhsDeviceIncomingParams(**payload)


def check_for_existing_mhs(data, cache):
product_team: ProductTeam = data[read_product_team]
product: CpmProduct = data[read_product]

device_repo = DeviceRepository(
table_name=cache["DYNAMODB_TABLE"], dynamodb_client=cache["DYNAMODB_CLIENT"]
)

devices = device_repo.search(product_team_id=product_team.id, product_id=product.id)
if any(device.name == MHS_DEVICE_NAME for device in devices):
raise ConfigurationError(
"There is already an existing MHS Device for this Product"
)


def read_device_reference_data(data, cache) -> DeviceReferenceData:
path_params: CpmProductPathParams = data[parse_path_params]
drd_repo = DeviceReferenceDataRepository(
table_name=cache["DYNAMODB_TABLE"], dynamodb_client=cache["DYNAMODB_CLIENT"]
)
device_reference_datas = drd_repo.search(
product_id=path_params.product_id,
product_team_id=path_params.product_team_id,
)

party_key: str = data[get_party_key]
# use {QuestionnaireInstance.SPINE_MHS_MESSAGE_SETS}
mhs_message_set_drd_name = f"{party_key} - MHS Message Set"

try:
(device_reference_data,) = filter(
lambda drd: drd.name == mhs_message_set_drd_name, device_reference_datas
)
except ValueError:
raise ConfigurationError(
"You must configure exactly one MessageSet Device Reference Data before creating an MHS Device"
)
return device_reference_data


def read_spine_mhs_questionnaire(data, cache) -> Questionnaire:
return QuestionnaireRepository().read(QuestionnaireInstance.SPINE_MHS)


def validate_spine_mhs_questionnaire_response(data, cache) -> QuestionnaireResponse:
spine_mhs_questionnaire: Questionnaire = data[read_spine_mhs_questionnaire]
payload: CreateMhsDeviceIncomingParams = data[parse_mhs_device_payload]
questionnaire_responses = payload.questionnaire_responses

# Ensure there's a questionnaire named 'spine_mhs' in the responses
if QuestionnaireInstance.SPINE_MHS not in questionnaire_responses:
raise InvalidSpineMhsResponse(
"Require a 'spine_mhs' questionnaire response to create a MHS Device"
)

raw_spine_mhs_questionnaire_response = payload.questionnaire_responses[
spine_mhs_questionnaire_response = payload.questionnaire_responses[
QuestionnaireInstance.SPINE_MHS
]
# Ensure there's only one response to 'spine_mhs'
if len(raw_spine_mhs_questionnaire_response) != 1:
raise InvalidSpineMhsResponse(
"Expected only one response for the 'spine_mhs' questionnaire"
)

return spine_mhs_questionnaire.validate(
data=raw_spine_mhs_questionnaire_response[0]
data=spine_mhs_questionnaire_response.__root__[0]
)


Expand All @@ -64,32 +105,63 @@ def create_mhs_device(data, cache) -> Device:
return product.create_device(**device_payload)


def create_party_key_tag(data, cache):
def create_party_key_tag(data, cache) -> DeviceTagAddedEvent:
mhs_device: Device = data[create_mhs_device]
return mhs_device.add_tag(party_key=data[get_party_key])


def create_cpa_id_keys(data, cache) -> DeviceKeyAddedEvent:
mhs_device: Device = data[create_mhs_device]
mhs_device.add_tag(party_key=data[get_party_key])
party_key = data[get_party_key]
drd: DeviceReferenceData = data[read_device_reference_data]
interaction_ids = []

# Extract Interaction IDs from questionnaire responses
questionnaire_responses = drd.questionnaire_responses.get(
f"{QuestionnaireInstance.SPINE_MHS_MESSAGE_SETS}/1", []
)
for response in questionnaire_responses:
interaction_ids.append(response.data.get("Interaction ID"))

# Use cpa_id in furture
for id in interaction_ids:
mhs_device.add_key(
key_type=DeviceKeyType.INTERACTION_ID, key_value=f"{party_key}:{id}"
)

return mhs_device


def add_spine_mhs_questionnaire_response(data, cache) -> list[QuestionnaireResponse]:
def add_device_reference_data_id(data, cache) -> DeviceReferenceDataIdAddedEvent:
mhs_device: Device = data[create_mhs_device]
drd: DeviceReferenceData = data[read_device_reference_data]
return mhs_device.add_device_reference_data_id(
device_reference_data_id=str(drd.id), path_to_data=["*"]
)


def add_spine_mhs_questionnaire_response(
data, cache
) -> QuestionnaireResponseUpdatedEvent:
spine_mhs_questionnaire_response: QuestionnaireResponse = data[
validate_spine_mhs_questionnaire_response
]
mhs_device: Device = data[create_party_key_tag]
mhs_device.add_questionnaire_response(spine_mhs_questionnaire_response)
return mhs_device
mhs_device: Device = data[create_mhs_device]

return mhs_device.add_questionnaire_response(spine_mhs_questionnaire_response)


def write_device(data: dict[str, CpmProduct], cache) -> CpmProduct:
mhs_device: Device = data[add_spine_mhs_questionnaire_response]
def write_device(data: dict[str, Device], cache) -> Device:
mhs_device: Device = data[create_mhs_device]
repo = DeviceRepository(
table_name=cache["DYNAMODB_TABLE"], dynamodb_client=cache["DYNAMODB_CLIENT"]
)
return repo.write(mhs_device)


def set_http_status(data, cache) -> tuple[HTTPStatus, str]:
device: Device = data[create_mhs_device]
return HTTPStatus.CREATED, device.state_exclude_tags()
def set_http_status(data, cache) -> tuple[HTTPStatus, dict]:
mhs_device: Device = data[create_mhs_device]
return HTTPStatus.CREATED, mhs_device.state_exclude_tags()


steps = [
Expand All @@ -99,10 +171,14 @@ def set_http_status(data, cache) -> tuple[HTTPStatus, str]:
read_product_team,
read_product,
get_party_key,
check_for_existing_mhs,
read_device_reference_data,
read_spine_mhs_questionnaire,
validate_spine_mhs_questionnaire_response,
create_mhs_device,
create_party_key_tag,
create_cpa_id_keys,
add_device_reference_data_id,
add_spine_mhs_questionnaire_response,
write_device,
set_http_status,
Expand Down
Loading
Loading