From 23dc9dc88d91b963eae4d995b7d38c78b3af4fdb Mon Sep 17 00:00:00 2001 From: Graham Lee Date: Thu, 23 Jun 2022 15:01:28 +0100 Subject: [PATCH] validate case #2714 --- .../controller/case_controller.py | 22 +++++++++++++++++-- .../reusable_data_service/main.py | 6 ++++- .../tests/test_case_controller.py | 13 +++++++++++ .../tests/test_case_end_to_end.py | 9 ++++++++ 4 files changed, 47 insertions(+), 3 deletions(-) diff --git a/data-serving/reusable-data-service/reusable_data_service/controller/case_controller.py b/data-serving/reusable-data-service/reusable_data_service/controller/case_controller.py index 35e3e4cce..7792e53ee 100644 --- a/data-serving/reusable-data-service/reusable_data_service/controller/case_controller.py +++ b/data-serving/reusable-data-service/reusable_data_service/controller/case_controller.py @@ -63,8 +63,7 @@ def create_case(self, maybe_case: dict, num_cases: int = 1): if num_cases <= 0: return "Must create a positive number of cases", 400 try: - case = Case.from_dict(maybe_case) - self.check_case_preconditions(case) + case = self.create_case_if_valid(maybe_case) for i in range(num_cases): self.store.insert_case(case) return "", 201 @@ -75,6 +74,25 @@ def create_case(self, maybe_case: dict, num_cases: int = 1): # PreconditionError means it's a case, but not one we can use return pe.args[0], 422 + def validate_case_dictionary(self, maybe_case: dict): + """Check whether a case _could_ be valid, without storing it if it is.""" + try: + case = self.create_case_if_valid(maybe_case) + return "", 204 + except ValueError as ve: + # ValueError means we can't even turn this into a case + return ve.args[0], 400 + except PreconditionError as pe: + # PreconditionError means it's a case, but not one we can use + return pe.args[0], 422 + + def create_case_if_valid(self, maybe_case: dict): + """Attempts to create a case from an input dictionary and validate it against + the application rules. Raises ValueError or PreconditionError on invalid input.""" + case = Case.from_dict(maybe_case) + self.check_case_preconditions(case) + return case + def check_case_preconditions(self, case: Case): if case.confirmation_date < self.outbreak_date: raise PreconditionError("Confirmation date is before outbreak began") diff --git a/data-serving/reusable-data-service/reusable_data_service/main.py b/data-serving/reusable-data-service/reusable_data_service/main.py index 33762c702..bd399a44c 100644 --- a/data-serving/reusable-data-service/reusable_data_service/main.py +++ b/data-serving/reusable-data-service/reusable_data_service/main.py @@ -25,10 +25,14 @@ def list_cases(): filter = request.args.get("q", type=str) return case_controller.list_cases(page=page, limit=limit, filter=filter) else: + potential_case = request.get_json() + validate_only = request.args.get("validate_only", type=bool) + if validate_only: + return case_controller.validate_case_dictionary(potential_case) count = request.args.get("num_cases", type=int) if count is None: count = 1 - return case_controller.create_case(request.get_json(), num_cases=count) + return case_controller.create_case(potential_case, num_cases=count) def set_up_controllers(): global case_controller diff --git a/data-serving/reusable-data-service/tests/test_case_controller.py b/data-serving/reusable-data-service/tests/test_case_controller.py index f09040d60..a13f2b959 100644 --- a/data-serving/reusable-data-service/tests/test_case_controller.py +++ b/data-serving/reusable-data-service/tests/test_case_controller.py @@ -140,3 +140,16 @@ def test_create_valid_case_with_positive_count_adds_to_collection(case_controlle ) assert status == 201 assert case_controller.store.count_cases() == 7 + + +def test_validate_case_with_invalid_case_is_400_error(case_controller): + (response, status) = case_controller.validate_case_dictionary({}) + assert status == 400 + + +def test_validate_case_with_valid_case_returns_204_and_does_not_add_case(case_controller): + (response, status) = case_controller.validate_case_dictionary( + {"confirmation_date": date(2021, 6, 3)} + ) + assert status == 204 + assert case_controller.store.count_cases() == 0 diff --git a/data-serving/reusable-data-service/tests/test_case_end_to_end.py b/data-serving/reusable-data-service/tests/test_case_end_to_end.py index 6184265dd..ba0e47e7e 100644 --- a/data-serving/reusable-data-service/tests/test_case_end_to_end.py +++ b/data-serving/reusable-data-service/tests/test_case_end_to_end.py @@ -174,3 +174,12 @@ def test_post_multiple_case_list_cases_round_trip(client_with_patched_mongo): assert get_response.status_code == 200 assert len(get_response.json["cases"]) == 3 assert get_response.json["cases"][0]["confirmation_date"] == "2022-01-23" + +def test_post_case_validate_only(client_with_patched_mongo): + post_response = client_with_patched_mongo.post("/api/cases?validate_only=true", json = { + "confirmation_date": "2022-01-23T13:45:01.234Z" + }) + assert post_response.status_code == 204 + get_response = client_with_patched_mongo.get("/api/cases") + assert get_response.status_code == 200 + assert len(get_response.json["cases"]) == 0