diff --git a/app/service/validation.py b/app/service/validation.py index 439020fb..85658029 100644 --- a/app/service/validation.py +++ b/app/service/validation.py @@ -239,4 +239,36 @@ def validate_bulk_import_data(data: dict[str, Any]) -> None: if not data: raise HTTPException(status_code=status.HTTP_204_NO_CONTENT) + validate_metadata_values_are_strings(data) validate_entity_relationships(data) + + +def _validate_values_are_strings(value: Any) -> None: + """ + Recursively validates that all single values are strings. + + :param Any value: The value to be validated. + :raises ValidationError: raised if any single value is not a string. + """ + if isinstance(value, list): + for v in value: + _validate_values_are_strings(v) + elif not isinstance(value, str): + raise ValidationError("Metadata values should be strings") + + +def validate_metadata_values_are_strings(data: dict[str, Any]) -> None: + """ + Validates any metadata in the data. + + :param dict[str, Any] data: The data object to be validated. + """ + metadata_values = [ + value + for entity in data.values() + if entity is not None + for e in entity + for value in e.get("metadata", {}).values() + ] + + _validate_values_are_strings(metadata_values) diff --git a/pyproject.toml b/pyproject.toml index 0f5ba2ff..cc17c644 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "admin_backend" -version = "2.17.19" +version = "2.17.20" description = "" authors = ["CPR-dev-team "] packages = [{ include = "app" }, { include = "tests" }] diff --git a/tests/unit_tests/routers/bulk_import/test_bulk_import.py b/tests/unit_tests/routers/bulk_import/test_bulk_import.py index 13e7360f..91d560cf 100644 --- a/tests/unit_tests/routers/bulk_import/test_bulk_import.py +++ b/tests/unit_tests/routers/bulk_import/test_bulk_import.py @@ -118,3 +118,20 @@ def test_bulk_import_documents_when_no_family( assert response.status_code == status.HTTP_400_BAD_REQUEST assert response.json().get("detail") == "No entity with id test.new.family.0 found" + + +def test_bulk_import_when_metadata_contains_non_string_values( + client: TestClient, superuser_header_token +): + json_input = build_json_file( + {"families": [{**default_family, "metadata": {"key": [1]}}]} + ) + + response = client.post( + "/api/v1/bulk-import/test", + files={"data": json_input}, + headers=superuser_header_token, + ) + + assert response.status_code == status.HTTP_400_BAD_REQUEST + assert response.json().get("detail") == "Metadata values should be strings" diff --git a/tests/unit_tests/service/validation/test_metadata_validation.py b/tests/unit_tests/service/validation/test_metadata_validation.py new file mode 100644 index 00000000..0d3ea5e5 --- /dev/null +++ b/tests/unit_tests/service/validation/test_metadata_validation.py @@ -0,0 +1,38 @@ +import pytest + +from app.errors import ValidationError +from app.service.validation import validate_metadata_values_are_strings + + +@pytest.mark.parametrize( + "test_data", + [ + {"entity1": [{"metadata": {"key1": ["value"], "key2": [""]}}]}, + {"entity2": [{"other_key": {"key1": ["value"]}}]}, + {"entity3": [{}]}, + {"entity4": []}, + {"entity5": ""}, + {"entity6": None}, + {}, + ], +) +def test_validate_metadata_when_ok(test_data): + + validate_metadata_values_are_strings(test_data) + + +@pytest.mark.parametrize( + "test_data", + [ + {"entity": [{"metadata": {"key": [1]}}]}, + {"entity": [{"metadata": {"key": [1]}}]}, + {"entity": [{"metadata": {"key": 1}}]}, + {"entity": [{"metadata": {"key": [None]}}]}, + {"entity": [{"metadata": {"key": None}}]}, + ], +) +def test_validate_metadata_throws_exception_when_non_string_values_present(test_data): + + with pytest.raises(ValidationError) as e: + validate_metadata_values_are_strings(test_data) + assert "Metadata values should be strings" == e.value.message