Skip to content

Commit d969d05

Browse files
Akol125mfjarvis
andauthored
VED-457: Refactor backend to include e-tag Header (#650)
* VED-457: Refactor backend to include e-tag Header * VED-457: change return type and e2e update response * VED-457: change return type and e2e update response json * VED-457: update imms call with etag parameter * VED-457: refactor e-tag headers * VED-457: refactor e-tag headers data type * VED-457: fix backoff error * VED-457: fix backoff error2 * VED-457: fix backoff error 3 * VED-457: fix backoff update error * VED-457: fix backoff update error2 * VED-457: fix e-tag * VED-457: fix e-tag2 * VED-457: Send correct E-Tag in update request. * VED-457: Fix lint error. * VED-457: Return updated version in two missed places. * VED-457: improve coverage test * VED-457: improve coverage for services and repository * VED-457: Remove unittest skip from test --------- Co-authored-by: Matt Jarvis <[email protected]>
1 parent d97be80 commit d969d05

File tree

10 files changed

+246
-65
lines changed

10 files changed

+246
-65
lines changed

backend/src/fhir_controller.py

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,8 @@ def create_immunization(self, aws_event):
197197
return self.create_response(400, json.dumps(exp_error))
198198
else:
199199
location = f"{get_service_url()}/Immunization/{resource.id}"
200-
return self.create_response(201, None, {"Location": location})
200+
version = "1"
201+
return self.create_response(201, None, {"Location": location, "E-Tag": version})
201202
except ValidationError as error:
202203
return self.create_response(400, error.to_operation_outcome())
203204
except IdentifierDuplicationError as duplicate:
@@ -224,7 +225,7 @@ def update_immunization(self, aws_event):
224225

225226
# Validate the imms id - start
226227
if id_error := self._validate_id(imms_id):
227-
return FhirController.create_response(400, json.dumps(id_error))
228+
return FhirController.create_response(400, json.dumps(id_error))
228229
# Validate the imms id - end
229230

230231
# Validate the body of the request - start
@@ -276,12 +277,17 @@ def update_immunization(self, aws_event):
276277
# Check vaccine type permissions on the existing record - end
277278

278279
existing_resource_version = int(existing_record["Version"])
280+
279281
try:
280282
# Validate if the imms resource to be updated is a logically deleted resource - start
281283
if existing_record["DeletedAt"] == True:
282-
283-
outcome, resource = self.fhir_service.reinstate_immunization(
284-
imms_id, imms, existing_resource_version, imms_vax_type_perms, supplier_system)
284+
outcome, resource, updated_version = self.fhir_service.reinstate_immunization(
285+
imms_id,
286+
imms,
287+
existing_resource_version,
288+
imms_vax_type_perms,
289+
supplier_system
290+
)
285291
# Validate if the imms resource to be updated is a logically deleted resource-end
286292
else:
287293
# Validate if imms resource version is part of the request - start
@@ -330,15 +336,15 @@ def update_immunization(self, aws_event):
330336

331337
# Check if the record is reinstated record - start
332338
if existing_record["Reinstated"] == True:
333-
outcome, resource = self.fhir_service.update_reinstated_immunization(
339+
outcome, resource, updated_version = self.fhir_service.update_reinstated_immunization(
334340
imms_id,
335341
imms,
336342
existing_resource_version,
337343
imms_vax_type_perms,
338344
supplier_system
339345
)
340346
else:
341-
outcome, resource = self.fhir_service.update_immunization(
347+
outcome, resource, updated_version = self.fhir_service.update_immunization(
342348
imms_id,
343349
imms,
344350
existing_resource_version,
@@ -358,7 +364,7 @@ def update_immunization(self, aws_event):
358364
)
359365
return self.create_response(400, json.dumps(exp_error))
360366
if outcome == UpdateOutcome.UPDATE:
361-
return self.create_response(200)
367+
return self.create_response(200, {"E-Tag": updated_version}) #include e-tag here, is it not included in the response resource
362368
except ValidationError as error:
363369
return self.create_response(400, error.to_operation_outcome())
364370
except IdentifierDuplicationError as duplicate:
@@ -541,7 +547,7 @@ def _create_bad_request(self, message):
541547
)
542548
return self.create_response(400, error)
543549

544-
550+
545551
def authorize_request(self, aws_event: dict) -> Optional[dict]:
546552
try:
547553
self.authorizer.authorize(aws_event)
@@ -667,5 +673,5 @@ def create_response(status_code, body=None, headers=None):
667673
def _identify_supplier_system(aws_event):
668674
supplier_system = aws_event["headers"]["SupplierSystem"]
669675
if not supplier_system:
670-
return self.create_response(403, unauthorized.to_operation_outcome())
676+
raise UnauthorizedError("SupplierSystem header is missing")
671677
return supplier_system

backend/src/fhir_repository.py

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import time
44
import uuid
55
from dataclasses import dataclass
6-
from typing import Optional
6+
from typing import Optional, Tuple
77

88
import boto3
99
import botocore.exceptions
@@ -194,9 +194,9 @@ def update_immunization(
194194
immunization: dict,
195195
patient: any,
196196
existing_resource_version: int,
197-
imms_vax_type_perms: str,
197+
imms_vax_type_perms: list[str],
198198
supplier_system: str,
199-
) -> dict:
199+
) -> tuple[dict, int]:
200200
attr = RecordAttributes(immunization, patient)
201201
self._handle_permissions(imms_vax_type_perms, attr)
202202
update_exp = self._build_update_expression(is_reinstate=False)
@@ -219,10 +219,10 @@ def reinstate_immunization(
219219
immunization: dict,
220220
patient: any,
221221
existing_resource_version: int,
222-
imms_vax_type_perms: str,
222+
imms_vax_type_perms: list[str],
223223
supplier_system: str,
224224

225-
) -> dict:
225+
) -> tuple[dict, int]:
226226
attr = RecordAttributes(immunization, patient)
227227
self._handle_permissions(imms_vax_type_perms, attr)
228228
update_exp = self._build_update_expression(is_reinstate=True)
@@ -245,9 +245,9 @@ def update_reinstated_immunization(
245245
immunization: dict,
246246
patient: any,
247247
existing_resource_version: int,
248-
imms_vax_type_perms: str,
248+
imms_vax_type_perms: list[str],
249249
supplier_system: str,
250-
) -> dict:
250+
) -> tuple[dict, int]:
251251
attr = RecordAttributes(immunization, patient)
252252
self._handle_permissions(imms_vax_type_perms, attr)
253253
update_exp = self._build_update_expression(is_reinstate=False)
@@ -299,8 +299,9 @@ def _perform_dynamo_update(
299299
supplier_system: str,
300300
deleted_at_required: bool,
301301
update_reinstated: bool,
302-
) -> dict:
302+
) -> Tuple[dict, int]:
303303
try:
304+
updated_version = existing_resource_version + 1
304305
condition_expression = Attr("PK").eq(attr.pk) & (
305306
Attr("DeletedAt").exists()
306307
if deleted_at_required
@@ -313,7 +314,7 @@ def _perform_dynamo_update(
313314
":patient_sk": attr.patient_sk,
314315
":imms_resource_val": json.dumps(attr.resource, use_decimal=True),
315316
":operation": "UPDATE",
316-
":version": existing_resource_version + 1,
317+
":version": updated_version,
317318
":supplier_system": supplier_system,
318319
":respawn": "reinstated",
319320
}
@@ -324,7 +325,7 @@ def _perform_dynamo_update(
324325
":patient_sk": attr.patient_sk,
325326
":imms_resource_val": json.dumps(attr.resource, use_decimal=True),
326327
":operation": "UPDATE",
327-
":version": existing_resource_version + 1,
328+
":version": updated_version,
328329
":supplier_system": supplier_system,
329330
}
330331

@@ -338,7 +339,7 @@ def _perform_dynamo_update(
338339
ReturnValues="ALL_NEW",
339340
ConditionExpression=condition_expression,
340341
)
341-
return self._handle_dynamo_response(response)
342+
return self._handle_dynamo_response(response), updated_version
342343
except botocore.exceptions.ClientError as error:
343344
# Either resource didn't exist or it has already been deleted. See ConditionExpression in the request
344345
if error.response["Error"]["Code"] == "ConditionalCheckFailedException":
@@ -352,12 +353,12 @@ def _perform_dynamo_update(
352353
def delete_immunization(
353354
self, imms_id: str, imms_vax_type_perms: str, supplier_system: str) -> dict:
354355
now_timestamp = int(time.time())
355-
356+
356357
try:
357358
item = self.table.get_item(Key={"PK": _make_immunization_pk(imms_id)}).get("Item")
358359
if not item:
359360
raise ResourceNotFoundError(resource_type="Immunization", resource_id=imms_id)
360-
361+
361362
if not item.get("DeletedAt") or item.get("DeletedAt") == "reinstated":
362363
vaccine_type = self._vaccine_type(item["PatientSK"])
363364
if not validate_permissions(imms_vax_type_perms, ApiOperationCode.DELETE, [vaccine_type]):
@@ -380,7 +381,7 @@ def delete_immunization(
380381
(Attr("DeletedAt").not_exists() | Attr("DeletedAt").eq("reinstated"))
381382
),
382383
)
383-
384+
384385
return self._handle_dynamo_response(response)
385386
except botocore.exceptions.ClientError as error:
386387
if error.response["Error"]["Code"] == "ConditionalCheckFailedException":

backend/src/fhir_service.py

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -136,13 +136,13 @@ def update_immunization(
136136
existing_resource_version: int,
137137
imms_vax_type_perms: list[str],
138138
supplier_system: str,
139-
) -> tuple[UpdateOutcome, Immunization]:
139+
) -> tuple[UpdateOutcome, Immunization, int]:
140140
immunization["id"] = imms_id
141141

142142
patient = self._validate_patient(immunization)
143143
if "diagnostics" in patient:
144-
return (None, patient)
145-
imms = self.immunization_repo.update_immunization(
144+
return (None, patient, None)
145+
imms, updated_version = self.immunization_repo.update_immunization(
146146
imms_id,
147147
immunization,
148148
patient,
@@ -151,7 +151,7 @@ def update_immunization(
151151
supplier_system
152152
)
153153

154-
return UpdateOutcome.UPDATE, Immunization.parse_obj(imms)
154+
return UpdateOutcome.UPDATE, Immunization.parse_obj(imms), updated_version
155155

156156
def reinstate_immunization(
157157
self,
@@ -160,12 +160,12 @@ def reinstate_immunization(
160160
existing_resource_version: int,
161161
imms_vax_type_perms: list[str],
162162
supplier_system: str,
163-
) -> tuple[UpdateOutcome, Immunization]:
163+
) -> tuple[UpdateOutcome, Immunization, int]:
164164
immunization["id"] = imms_id
165165
patient = self._validate_patient(immunization)
166166
if "diagnostics" in patient:
167-
return (None, patient)
168-
imms = self.immunization_repo.reinstate_immunization(
167+
return (None, patient, None)
168+
imms, updated_version = self.immunization_repo.reinstate_immunization(
169169
imms_id,
170170
immunization,
171171
patient,
@@ -174,7 +174,7 @@ def reinstate_immunization(
174174
supplier_system,
175175
)
176176

177-
return UpdateOutcome.UPDATE, Immunization.parse_obj(imms)
177+
return UpdateOutcome.UPDATE, Immunization.parse_obj(imms), updated_version
178178

179179
def update_reinstated_immunization(
180180
self,
@@ -183,14 +183,12 @@ def update_reinstated_immunization(
183183
existing_resource_version: int,
184184
imms_vax_type_perms: list[str],
185185
supplier_system: str,
186-
) -> tuple[UpdateOutcome, Immunization]:
186+
) -> tuple[UpdateOutcome, Immunization, int]:
187187
immunization["id"] = imms_id
188-
189-
patient = None
190188
patient = self._validate_patient(immunization)
191189
if "diagnostics" in patient:
192-
return (None, patient)
193-
imms = self.immunization_repo.update_reinstated_immunization(
190+
return (None, patient, None)
191+
imms, updated_version = self.immunization_repo.update_reinstated_immunization(
194192
imms_id,
195193
immunization,
196194
patient,
@@ -199,7 +197,7 @@ def update_reinstated_immunization(
199197
supplier_system,
200198
)
201199

202-
return UpdateOutcome.UPDATE, Immunization.parse_obj(imms)
200+
return UpdateOutcome.UPDATE, Immunization.parse_obj(imms), updated_version
203201

204202
def delete_immunization(self, imms_id, imms_vax_type_perms, supplier_system) -> Immunization:
205203
"""

0 commit comments

Comments
 (0)