Skip to content

Commit 30a8c81

Browse files
megan-bower4jameslinnell
authored andcommitted
AS Device read unit tests
AS Device read feature tests
1 parent e947cde commit 30a8c81

26 files changed

+2103
-26
lines changed

infrastructure/swagger/05_paths.yaml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,32 @@ paths:
385385
- ${authoriser_name}: []
386386
- app-level0: []
387387

388+
/ProductTeam/{product_team_id}/Product/{product_id}/Device/AccreditedSystem:
389+
post:
390+
operationId: createDeviceAccreditedSystem
391+
summary: Create a Accredited System Device resource (POST)
392+
parameters:
393+
- $ref: "#/components/parameters/ProductTeamId"
394+
- $ref: "#/components/parameters/ProductId"
395+
- $ref: "#/components/parameters/HeaderVersion"
396+
- $ref: "#/components/parameters/HeaderRequestId"
397+
- $ref: "#/components/parameters/HeaderCorrelationId"
398+
requestBody:
399+
$ref: "#/components/requestBodies/AccreditedSystemDeviceCreateRequestBody"
400+
responses:
401+
"201":
402+
$ref: "#/components/responses/AsDeviceCreate"
403+
"400":
404+
$ref: "#/components/responses/BadRequest"
405+
"404":
406+
$ref: "#/components/responses/NotFound"
407+
x-amazon-apigateway-integration:
408+
<<: *ApiGatewayIntegration
409+
uri: ${method_createDeviceAccreditedSystem}
410+
security:
411+
- ${authoriser_name}: []
412+
- app-level0: []
413+
388414
/searchSdsDevice:
389415
get:
390416
operationId: searchsdsdevice

infrastructure/swagger/07_components--schemas--domain.yaml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,32 @@ components:
167167
questionnaire_responses:
168168
type: object
169169

170+
AsDeviceResponse:
171+
type: object
172+
properties:
173+
id:
174+
type: string
175+
name:
176+
type: string
177+
product_id:
178+
type: string
179+
product_team_id:
180+
type: string
181+
ods_code:
182+
type: string
183+
status:
184+
type: string
185+
created_on:
186+
type: string
187+
updated_on:
188+
type: string
189+
nullable: true
190+
deleted_on:
191+
type: string
192+
nullable: true
193+
questionnaire_responses:
194+
type: object
195+
170196
MhsDeviceResponse:
171197
type: object
172198
properties:

infrastructure/swagger/11_components--requestBodies.yaml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,3 +144,25 @@ components:
144144
- questionnaire_responses
145145
example:
146146
questionnaire_responses: { "spine_mhs": [{ "question": "answer" }] }
147+
AccreditedSystemDeviceCreateRequestBody:
148+
description: Request body to create a Accredited System Device object
149+
required: true
150+
content:
151+
application/json:
152+
schema:
153+
type: object
154+
properties:
155+
questionnaire_responses:
156+
type: object
157+
description: Questionnaire Responses for AS Device
158+
properties:
159+
spine_as:
160+
type: array
161+
description: spine_as questionnaire associated with the as device
162+
items:
163+
type: object
164+
description: spine_as questionnaire response
165+
required:
166+
- questionnaire_responses
167+
example:
168+
questionnaire_responses: { "spine_as": [{ "question": "answer" }] }

infrastructure/swagger/12_components--responses.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,12 @@ components:
165165
application/json:
166166
schema:
167167
$ref: "#/components/schemas/DeviceResponse"
168+
AsDeviceCreate:
169+
description: Create Accredited System Device operation successful
170+
content:
171+
application/json:
172+
schema:
173+
$ref: "#/components/schemas/AsDeviceResponse"
168174
MhsDeviceCreate:
169175
description: Create Message Handling System Device operation successful
170176
content:
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
from api_utils.api_step_chain import execute_step_chain
2+
from event.aws.client import dynamodb_client
3+
from event.environment import BaseEnvironment
4+
from event.logging.logger import setup_logger
5+
6+
from .src.v1.steps import steps as v1_steps
7+
8+
9+
class Environment(BaseEnvironment):
10+
DYNAMODB_TABLE: str
11+
12+
13+
versioned_steps = {"1": v1_steps}
14+
cache = {
15+
**Environment.build().dict(),
16+
"DYNAMODB_CLIENT": dynamodb_client(),
17+
}
18+
19+
20+
def handler(event: dict, context=None):
21+
setup_logger(service_name=__file__)
22+
return execute_step_chain(
23+
event=event,
24+
cache=cache,
25+
versioned_steps=versioned_steps,
26+
)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
from builder.lambda_build import build
2+
3+
if __name__ == "__main__":
4+
build(__file__)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[
2+
"dynamodb:Query",
3+
"dynamodb:PutItem",
4+
"dynamodb:UpdateItem",
5+
"dynamodb:GetItem"
6+
]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
["kms:Decrypt"]
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
from http import HTTPStatus
2+
3+
from domain.api.common_steps.general import parse_event_body
4+
from domain.api.common_steps.read_product import (
5+
parse_path_params,
6+
read_product,
7+
read_product_team,
8+
)
9+
10+
# from domain.core import device
11+
from domain.core.cpm_product import CpmProduct
12+
from domain.core.device import ( # DeviceTagAddedEvent,;; DeviceKeyAddedEvent,; DeviceReferenceDataIdAddedEvent,
13+
Device,
14+
DeviceTagAddedEvent,
15+
QuestionnaireResponseUpdatedEvent,
16+
)
17+
18+
# from domain.core.device_key.v1 import DeviceKeyType
19+
from domain.core.device_reference_data import DeviceReferenceData
20+
from domain.core.error import ( # InvalidSpineAsResponse,
21+
AccreditedSystemFatalError,
22+
ConfigurationError,
23+
NotEprProductError,
24+
)
25+
from domain.core.product_key import ProductKeyType
26+
from domain.core.questionnaire import Questionnaire, QuestionnaireResponse
27+
from domain.repository.device_reference_data_repository import (
28+
DeviceReferenceDataRepository,
29+
)
30+
from domain.repository.device_repository import DeviceRepository
31+
from domain.repository.questionnaire_repository import (
32+
QuestionnaireInstance,
33+
QuestionnaireRepository,
34+
)
35+
from domain.request_models import CpmProductPathParams, CreateAsDeviceIncomingParams
36+
from domain.response.validation_errors import mark_validation_errors_as_inbound
37+
38+
39+
@mark_validation_errors_as_inbound
40+
def parse_as_device_payload(data, cache) -> CreateAsDeviceIncomingParams:
41+
payload: dict = data[parse_event_body]
42+
return CreateAsDeviceIncomingParams(**payload)
43+
44+
45+
def read_device_reference_data(data, cache) -> list[DeviceReferenceData]:
46+
path_params: CpmProductPathParams = data[parse_path_params]
47+
product_repo = DeviceReferenceDataRepository(
48+
table_name=cache["DYNAMODB_TABLE"], dynamodb_client=cache["DYNAMODB_CLIENT"]
49+
)
50+
device_reference_data = product_repo.search(
51+
product_id=path_params.product_id,
52+
product_team_id=path_params.product_team_id,
53+
)
54+
55+
# Only 1 AS DRD and only 1 MHS DRD
56+
57+
if not device_reference_data or len(device_reference_data) < 2:
58+
raise ConfigurationError(
59+
"You must configure the AS and MessageSet Device Reference Data before creating an AS Device"
60+
)
61+
62+
if len(device_reference_data) > 2:
63+
raise AccreditedSystemFatalError(
64+
"More that 2 MessageSet Device Reference Data resources were found. This is not allowed"
65+
)
66+
67+
exists = set()
68+
if any(drd.name in exists or exists.add(drd.name) for drd in device_reference_data):
69+
raise ConfigurationError(
70+
"Only one AS and one MessageSet Device Reference Data is allowed before creating an AS Device"
71+
)
72+
73+
return device_reference_data
74+
75+
76+
def read_spine_as_questionnaire(data, cache) -> Questionnaire:
77+
return QuestionnaireRepository().read(QuestionnaireInstance.SPINE_AS)
78+
79+
80+
def validate_spine_as_questionnaire_response(data, cache) -> QuestionnaireResponse:
81+
spine_as_questionnaire: Questionnaire = data[read_spine_as_questionnaire]
82+
payload: CreateAsDeviceIncomingParams = data[parse_as_device_payload]
83+
spine_as_questionnaire_response = payload.questionnaire_responses[
84+
QuestionnaireInstance.SPINE_AS
85+
]
86+
return spine_as_questionnaire.validate(
87+
data=spine_as_questionnaire_response.__root__[0]
88+
)
89+
90+
91+
def create_as_device(data, cache) -> Device:
92+
product: CpmProduct = data[read_product]
93+
payload: CreateAsDeviceIncomingParams = data[parse_as_device_payload]
94+
95+
# Create a new Device dictionary excluding 'questionnaire_responses'
96+
device_payload = payload.dict(exclude={"questionnaire_responses"})
97+
return product.create_device(**device_payload)
98+
99+
100+
def create_party_key_tag(data, cache) -> DeviceTagAddedEvent:
101+
as_device: Device = data[create_as_device]
102+
return as_device.add_tag(party_key=data[get_party_key])
103+
104+
105+
def create_device_keys(data, cache) -> Device:
106+
# We will need to add some keys in the future, ASID?
107+
as_device: Device = data[create_as_device]
108+
return as_device
109+
110+
111+
def add_device_reference_data_id(data, cache) -> Device:
112+
as_device: Device = data[create_device_keys]
113+
device_reference_data: DeviceReferenceData = data[read_device_reference_data]
114+
for drd in device_reference_data:
115+
as_device.add_device_reference_data_id(
116+
device_reference_data_id=str(drd.id), path_to_data=["*.interaction_ids"]
117+
)
118+
return as_device
119+
120+
121+
def add_spine_as_questionnaire_response(
122+
data, cache
123+
) -> QuestionnaireResponseUpdatedEvent:
124+
spine_as_questionnaire_response: QuestionnaireResponse = data[
125+
validate_spine_as_questionnaire_response
126+
]
127+
as_device: Device = data[create_as_device]
128+
129+
return as_device.add_questionnaire_response(spine_as_questionnaire_response)
130+
131+
132+
def write_device(data: dict[str, Device], cache) -> Device:
133+
as_device: Device = data[create_as_device]
134+
repo = DeviceRepository(
135+
table_name=cache["DYNAMODB_TABLE"], dynamodb_client=cache["DYNAMODB_CLIENT"]
136+
)
137+
return repo.write(as_device)
138+
139+
140+
def set_http_status(data, cache) -> tuple[HTTPStatus, dict]:
141+
as_device: Device = data[create_as_device]
142+
return HTTPStatus.CREATED, as_device.state_exclude_tags()
143+
144+
145+
def get_party_key(data, cache) -> str:
146+
product: CpmProduct = data[read_product]
147+
party_keys = (
148+
key.key_value
149+
for key in product.keys
150+
if key.key_type is ProductKeyType.PARTY_KEY
151+
)
152+
try:
153+
(party_key,) = party_keys
154+
except ValueError:
155+
raise NotEprProductError(
156+
"Not an EPR Product: Cannot create AS device for product without exactly one Party Key"
157+
)
158+
return party_key
159+
160+
161+
steps = [
162+
parse_event_body,
163+
parse_path_params,
164+
parse_as_device_payload,
165+
read_product_team,
166+
read_product,
167+
get_party_key,
168+
read_device_reference_data,
169+
read_spine_as_questionnaire,
170+
validate_spine_as_questionnaire_response,
171+
create_as_device,
172+
create_party_key_tag,
173+
create_device_keys,
174+
add_device_reference_data_id,
175+
add_spine_as_questionnaire_response,
176+
write_device,
177+
set_http_status,
178+
]

0 commit comments

Comments
 (0)