Skip to content

Commit 6633a17

Browse files
committed
[release/2024-11-19-a] Merge branch feature/PI-581-create_mhs_device_with_drd into release/2024-11-19-a
1 parent fadf0d2 commit 6633a17

File tree

17 files changed

+452
-122
lines changed

17 files changed

+452
-122
lines changed

src/api/createDevice/src/v1/steps.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515

1616
@mark_validation_errors_as_inbound
17-
def parse_device_payload(data, cache) -> Device:
17+
def parse_device_payload(data, cache) -> CreateDeviceIncomingParams:
1818
payload: dict = data[parse_event_body]
1919
return CreateDeviceIncomingParams(**payload)
2020

src/api/createDeviceMessageHandlingSystem/src/v1/steps.py

Lines changed: 105 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -8,50 +8,91 @@
88
read_product_team,
99
)
1010
from domain.core.cpm_product import CpmProduct
11-
from domain.core.device import Device
12-
from domain.core.error import InvalidSpineMhsResponse
11+
from domain.core.device import (
12+
MHS_DEVICE_NAME,
13+
Device,
14+
DeviceKeyAddedEvent,
15+
DeviceReferenceDataIdAddedEvent,
16+
DeviceTagAddedEvent,
17+
QuestionnaireResponseUpdatedEvent,
18+
)
19+
from domain.core.device_key import DeviceKeyType
20+
from domain.core.device_reference_data import DeviceReferenceData
21+
from domain.core.error import ConfigurationError
22+
from domain.core.product_team import ProductTeam
1323
from domain.core.questionnaire import Questionnaire, QuestionnaireResponse
24+
from domain.repository.device_reference_data_repository import (
25+
DeviceReferenceDataRepository,
26+
)
1427
from domain.repository.device_repository import DeviceRepository
1528
from domain.repository.questionnaire_repository import (
1629
QuestionnaireInstance,
1730
QuestionnaireRepository,
1831
)
19-
from domain.request_models import CreateMhsDeviceIncomingParams
32+
from domain.request_models import CpmProductPathParams, CreateMhsDeviceIncomingParams
2033
from domain.response.validation_errors import mark_validation_errors_as_inbound
2134

2235

2336
@mark_validation_errors_as_inbound
24-
def parse_mhs_device_payload(data, cache) -> Device:
37+
def parse_mhs_device_payload(data, cache) -> CreateMhsDeviceIncomingParams:
2538
payload: dict = data[parse_event_body]
2639
return CreateMhsDeviceIncomingParams(**payload)
2740

2841

42+
def check_for_existing_mhs(data, cache):
43+
product_team: ProductTeam = data[read_product_team]
44+
product: CpmProduct = data[read_product]
45+
46+
device_repo = DeviceRepository(
47+
table_name=cache["DYNAMODB_TABLE"], dynamodb_client=cache["DYNAMODB_CLIENT"]
48+
)
49+
50+
devices = device_repo.search(product_team_id=product_team.id, product_id=product.id)
51+
if any(device.name == MHS_DEVICE_NAME for device in devices):
52+
raise ConfigurationError(
53+
"There is already an existing MHS Device for this Product"
54+
)
55+
56+
57+
def read_device_reference_data(data, cache) -> DeviceReferenceData:
58+
path_params: CpmProductPathParams = data[parse_path_params]
59+
drd_repo = DeviceReferenceDataRepository(
60+
table_name=cache["DYNAMODB_TABLE"], dynamodb_client=cache["DYNAMODB_CLIENT"]
61+
)
62+
device_reference_datas = drd_repo.search(
63+
product_id=path_params.product_id,
64+
product_team_id=path_params.product_team_id,
65+
)
66+
67+
party_key: str = data[get_party_key]
68+
# use {QuestionnaireInstance.SPINE_MHS_MESSAGE_SETS}
69+
mhs_message_set_drd_name = f"{party_key} - MHS Message Set"
70+
71+
try:
72+
(device_reference_data,) = filter(
73+
lambda drd: drd.name == mhs_message_set_drd_name, device_reference_datas
74+
)
75+
except ValueError:
76+
raise ConfigurationError(
77+
"You must configure exactly one MessageSet Device Reference Data before creating an MHS Device"
78+
)
79+
return device_reference_data
80+
81+
2982
def read_spine_mhs_questionnaire(data, cache) -> Questionnaire:
3083
return QuestionnaireRepository().read(QuestionnaireInstance.SPINE_MHS)
3184

3285

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

38-
# Ensure there's a questionnaire named 'spine_mhs' in the responses
39-
if QuestionnaireInstance.SPINE_MHS not in questionnaire_responses:
40-
raise InvalidSpineMhsResponse(
41-
"Require a 'spine_mhs' questionnaire response to create a MHS Device"
42-
)
43-
44-
raw_spine_mhs_questionnaire_response = payload.questionnaire_responses[
90+
spine_mhs_questionnaire_response = payload.questionnaire_responses[
4591
QuestionnaireInstance.SPINE_MHS
4692
]
47-
# Ensure there's only one response to 'spine_mhs'
48-
if len(raw_spine_mhs_questionnaire_response) != 1:
49-
raise InvalidSpineMhsResponse(
50-
"Expected only one response for the 'spine_mhs' questionnaire"
51-
)
5293

5394
return spine_mhs_questionnaire.validate(
54-
data=raw_spine_mhs_questionnaire_response[0]
95+
data=spine_mhs_questionnaire_response.__root__[0]
5596
)
5697

5798

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

66107

67-
def create_party_key_tag(data, cache):
108+
def create_party_key_tag(data, cache) -> DeviceTagAddedEvent:
109+
mhs_device: Device = data[create_mhs_device]
110+
return mhs_device.add_tag(party_key=data[get_party_key])
111+
112+
113+
def create_cpa_id_keys(data, cache) -> DeviceKeyAddedEvent:
68114
mhs_device: Device = data[create_mhs_device]
69-
mhs_device.add_tag(party_key=data[get_party_key])
115+
party_key = data[get_party_key]
116+
drd: DeviceReferenceData = data[read_device_reference_data]
117+
interaction_ids = []
118+
119+
# Extract Interaction IDs from questionnaire responses
120+
questionnaire_responses = drd.questionnaire_responses.get(
121+
f"{QuestionnaireInstance.SPINE_MHS_MESSAGE_SETS}/1", []
122+
)
123+
for response in questionnaire_responses:
124+
interaction_ids.append(response.data.get("Interaction ID"))
125+
126+
# Use cpa_id in furture
127+
for id in interaction_ids:
128+
mhs_device.add_key(
129+
key_type=DeviceKeyType.INTERACTION_ID, key_value=f"{party_key}:{id}"
130+
)
131+
70132
return mhs_device
71133

72134

73-
def add_spine_mhs_questionnaire_response(data, cache) -> list[QuestionnaireResponse]:
135+
def add_device_reference_data_id(data, cache) -> DeviceReferenceDataIdAddedEvent:
136+
mhs_device: Device = data[create_mhs_device]
137+
drd: DeviceReferenceData = data[read_device_reference_data]
138+
return mhs_device.add_device_reference_data_id(
139+
device_reference_data_id=str(drd.id), path_to_data=["*"]
140+
)
141+
142+
143+
def add_spine_mhs_questionnaire_response(
144+
data, cache
145+
) -> QuestionnaireResponseUpdatedEvent:
74146
spine_mhs_questionnaire_response: QuestionnaireResponse = data[
75147
validate_spine_mhs_questionnaire_response
76148
]
77-
mhs_device: Device = data[create_party_key_tag]
78-
mhs_device.add_questionnaire_response(spine_mhs_questionnaire_response)
79-
return mhs_device
149+
mhs_device: Device = data[create_mhs_device]
150+
151+
return mhs_device.add_questionnaire_response(spine_mhs_questionnaire_response)
80152

81153

82-
def write_device(data: dict[str, CpmProduct], cache) -> CpmProduct:
83-
mhs_device: Device = data[add_spine_mhs_questionnaire_response]
154+
def write_device(data: dict[str, Device], cache) -> Device:
155+
mhs_device: Device = data[create_mhs_device]
84156
repo = DeviceRepository(
85157
table_name=cache["DYNAMODB_TABLE"], dynamodb_client=cache["DYNAMODB_CLIENT"]
86158
)
87159
return repo.write(mhs_device)
88160

89161

90-
def set_http_status(data, cache) -> tuple[HTTPStatus, str]:
91-
device: Device = data[create_mhs_device]
92-
return HTTPStatus.CREATED, device.state_exclude_tags()
162+
def set_http_status(data, cache) -> tuple[HTTPStatus, dict]:
163+
mhs_device: Device = data[create_mhs_device]
164+
return HTTPStatus.CREATED, mhs_device.state_exclude_tags()
93165

94166

95167
steps = [
@@ -99,10 +171,14 @@ def set_http_status(data, cache) -> tuple[HTTPStatus, str]:
99171
read_product_team,
100172
read_product,
101173
get_party_key,
174+
check_for_existing_mhs,
175+
read_device_reference_data,
102176
read_spine_mhs_questionnaire,
103177
validate_spine_mhs_questionnaire_response,
104178
create_mhs_device,
105179
create_party_key_tag,
180+
create_cpa_id_keys,
181+
add_device_reference_data_id,
106182
add_spine_mhs_questionnaire_response,
107183
write_device,
108184
set_http_status,

src/api/createDeviceMessageHandlingSystem/tests/test_index.py

Lines changed: 105 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,15 @@
1313
from domain.core.product_key import ProductKeyType
1414
from domain.core.root import Root
1515
from domain.repository.cpm_product_repository import CpmProductRepository
16+
from domain.repository.device_reference_data_repository import (
17+
DeviceReferenceDataRepository,
18+
)
1619
from domain.repository.device_repository import DeviceRepository
1720
from domain.repository.product_team_repository import ProductTeamRepository
21+
from domain.repository.questionnaire_repository import (
22+
QuestionnaireInstance,
23+
QuestionnaireRepository,
24+
)
1825
from event.json import json_loads
1926

2027
from test_helpers.dynamodb import mock_table
@@ -49,7 +56,9 @@
4956

5057

5158
@contextmanager
52-
def mock_epr_product() -> Generator[tuple[ModuleType, CpmProduct], Any, None]:
59+
def mock_epr_product_with_message_set_drd() -> (
60+
Generator[tuple[ModuleType, CpmProduct], Any, None]
61+
):
5362
org = Root.create_ods_organisation(ods_code=ODS_CODE)
5463
product_team = org.create_product_team(name=PRODUCT_TEAM_NAME)
5564

@@ -72,6 +81,37 @@ def mock_epr_product() -> Generator[tuple[ModuleType, CpmProduct], Any, None]:
7281
)
7382
product_repo.write(entity=product)
7483

84+
# set up questionnaire response
85+
mhs_message_set_questionnaire = QuestionnaireRepository().read(
86+
QuestionnaireInstance.SPINE_MHS_MESSAGE_SETS
87+
)
88+
questionnaire_response = mhs_message_set_questionnaire.validate(
89+
data={
90+
"Interaction ID": "urn:foo",
91+
"MHS SN": "bar",
92+
"MHS IN": "baz",
93+
}
94+
)
95+
96+
questionnaire_response_2 = mhs_message_set_questionnaire.validate(
97+
data={
98+
"Interaction ID": "urn:foo2",
99+
"MHS SN": "bar2",
100+
"MHS IN": "baz2",
101+
},
102+
)
103+
104+
# Set up DeviceReferenceData in DB
105+
device_reference_data = product.create_device_reference_data(
106+
name="ABC1234-987654 - MHS Message Set"
107+
)
108+
device_reference_data.add_questionnaire_response(questionnaire_response)
109+
device_reference_data.add_questionnaire_response(questionnaire_response_2)
110+
device_reference_data_repo = DeviceReferenceDataRepository(
111+
table_name=TABLE_NAME, dynamodb_client=client
112+
)
113+
device_reference_data_repo.write(device_reference_data)
114+
75115
import api.createDeviceMessageHandlingSystem.index as index
76116

77117
index.cache["DYNAMODB_CLIENT"] = client
@@ -109,8 +149,41 @@ def mock_not_epr_product() -> Generator[tuple[ModuleType, CpmProduct], Any, None
109149
yield index, product
110150

111151

152+
@contextmanager
153+
def mock_epr_product_without_message_set_drd() -> (
154+
Generator[tuple[ModuleType, CpmProduct], Any, None]
155+
):
156+
org = Root.create_ods_organisation(ods_code=ODS_CODE)
157+
product_team = org.create_product_team(name=PRODUCT_TEAM_NAME)
158+
159+
with mock_table(table_name=TABLE_NAME) as client, mock.patch.dict(
160+
os.environ,
161+
{"DYNAMODB_TABLE": TABLE_NAME, "AWS_DEFAULT_REGION": "eu-west-2"},
162+
clear=True,
163+
):
164+
product_team_repo = ProductTeamRepository(
165+
table_name=TABLE_NAME, dynamodb_client=client
166+
)
167+
product_team_repo.write(entity=product_team)
168+
169+
product = product_team.create_cpm_product(
170+
name=PRODUCT_NAME, product_id=PRODUCT_ID
171+
)
172+
product.add_key(key_type=ProductKeyType.PARTY_KEY, key_value="ABC1234-987654")
173+
product_repo = CpmProductRepository(
174+
table_name=TABLE_NAME, dynamodb_client=client
175+
)
176+
product_repo.write(entity=product)
177+
178+
import api.createDeviceMessageHandlingSystem.index as index
179+
180+
index.cache["DYNAMODB_CLIENT"] = client
181+
182+
yield index, product
183+
184+
112185
def test_index() -> None:
113-
with mock_epr_product() as (index, product):
186+
with mock_epr_product_with_message_set_drd() as (index, product):
114187
# Execute the lambda
115188
response = index.handler(
116189
event={
@@ -194,7 +267,7 @@ def test_index() -> None:
194267
],
195268
)
196269
def test_incoming_errors(body, path_parameters, error_code, status_code):
197-
with mock_epr_product() as (index, _):
270+
with mock_epr_product_with_message_set_drd() as (index, _):
198271
# Execute the lambda
199272
response = index.handler(
200273
event={
@@ -219,7 +292,7 @@ def test_incoming_errors(body, path_parameters, error_code, status_code):
219292
},
220293
},
221294
"VALIDATION_ERROR",
222-
"Expected only one response for the 'spine_mhs' questionnaire",
295+
"CreateMhsDeviceIncomingParams.questionnaire_responses.spine_mhs.__root__: ensure this value has at most 1 items",
223296
400,
224297
),
225298
(
@@ -237,7 +310,7 @@ def test_incoming_errors(body, path_parameters, error_code, status_code):
237310
def test_questionnaire_response_validation_errors(
238311
body, error_code, error_message, status_code
239312
):
240-
with mock_epr_product() as (index, product):
313+
with mock_epr_product_with_message_set_drd() as (index, product):
241314
# Execute the lambda
242315
response = index.handler(
243316
event={
@@ -274,6 +347,32 @@ def test_not_epr_product():
274347

275348
assert response["statusCode"] == 400
276349
expected_error_code = "VALIDATION_ERROR"
277-
expected_message_code = "Not an EPR Product: Cannot create MHS device for product without exactly one Party Key"
350+
expected_message_code = "Not an EPR Product: Cannot create MHS Device for product without exactly one Party Key"
278351
assert expected_error_code in response["body"]
279352
assert expected_message_code in response["body"]
353+
354+
355+
def test_no_existing_message_set_drd():
356+
with mock_epr_product_without_message_set_drd() as (index, product):
357+
# Execute the lambda
358+
response = index.handler(
359+
event={
360+
"headers": {"version": VERSION},
361+
"body": json.dumps(
362+
{"questionnaire_responses": {"spine_mhs": [QUESTIONNAIRE_DATA]}}
363+
),
364+
"pathParameters": {
365+
"product_team_id": str(product.product_team_id),
366+
"product_id": str(product.id),
367+
},
368+
}
369+
)
370+
371+
assert response["statusCode"] == 400
372+
expected_error_code = "VALIDATION_ERROR"
373+
expected_message_code = "You must configure exactly one MessageSet Device Reference Data before creating an MHS Device"
374+
assert expected_error_code in response["body"]
375+
assert expected_message_code in response["body"]
376+
377+
378+
# add test for already existing mhs device?

src/api/createDeviceReferenceData/src/v1/steps.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616

1717

1818
@mark_validation_errors_as_inbound
19-
def parse_device_reference_data_payload(data, cache) -> DeviceReferenceData:
19+
def parse_device_reference_data_payload(
20+
data, cache
21+
) -> CreateDeviceReferenceDataIncomingParams:
2022
payload: dict = data[parse_event_body]
2123
return CreateDeviceReferenceDataIncomingParams(**payload)
2224

src/api/readDevice/src/v1/steps.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ def read_product(data, cache) -> CpmProduct:
3434
table_name=cache["DYNAMODB_TABLE"], dynamodb_client=cache["DYNAMODB_CLIENT"]
3535
)
3636
cpm_product = product_repo.read(
37-
product_team_id=product_team.id, id=path_params.product_id
37+
id=path_params.product_id, product_team_id=product_team.id
3838
)
3939
return cpm_product
4040

0 commit comments

Comments
 (0)