diff --git a/greg/api/serializers/person.py b/greg/api/serializers/person.py index f4e5f2b29452ff926c536ebcd13c313aaa510551..43e3dbcf6f2f9dc6b7298e9c3a081b0c5ee5b569 100644 --- a/greg/api/serializers/person.py +++ b/greg/api/serializers/person.py @@ -37,3 +37,18 @@ class PersonIdentitySerializer(serializers.ModelSerializer): class Meta: model = PersonIdentity fields = "__all__" + + def is_duplicate(self, identity_type: str, value: str) -> bool: + # Guests may be verified using another unrecognised identification method, + # which the sponsor is required to elaborate in the value column. + # In this case we cannot assume the union of the identity type and + # the value to be unique across all records. + if identity_type == PersonIdentity.IdentityType.OTHER: + return False + + # If the type is a specific ID type, then duplicates are not expected + return ( + PersonIdentity.objects.filter(type__like=identity_type) + .filter(value__like=value) + .exists() + ) diff --git a/greg/api/views/person.py b/greg/api/views/person.py index 9918256b5f05aba0416213c34d3245d08728f21a..40f9c8d07fc1b8cb556046aa3536f3e21029cf5d 100644 --- a/greg/api/views/person.py +++ b/greg/api/views/person.py @@ -104,6 +104,10 @@ class PersonIdentityViewSet(viewsets.ModelViewSet): input_data["person"] = person_id serializer = self.get_serializer(data=input_data) + + if serializer.is_duplicate(input_data["type"], input_data["value"]): + raise ValidationError("Duplicate identity entry exists") + serializer.is_valid(raise_exception=True) self.perform_create(serializer) headers = self.get_success_headers(serializer.data) diff --git a/greg/tests/api/test_person.py b/greg/tests/api/test_person.py index f919737287e992b60c3bf061023a1702e5ee9849..211f141720cf461a9b43071dcce9ca32b4ee96f8 100644 --- a/greg/tests/api/test_person.py +++ b/greg/tests/api/test_person.py @@ -5,6 +5,8 @@ from rest_framework import status from rest_framework.reverse import reverse from rest_framework.status import HTTP_200_OK +from django.core.exceptions import ValidationError + from greg.models import ( PersonIdentity, Sponsor, @@ -220,6 +222,40 @@ def test_identity_add(client, person_foo): assert len(results) == 1 +@pytest.mark.django_db +def test_identity_add_duplicate(client, person_foo, person_bar): + data = { + "type": PersonIdentity.IdentityType.FEIDE_ID, + "source": "Test source", + "value": "12345", + } + client.post( + reverse("person_identity-list", kwargs={"person_id": person_bar.id}), data=data + ) + + with pytest.raises(ValidationError): + client.post( + reverse("person_identity-list", kwargs={"person_id": person_foo.id}), + data=data, + ) + + +@pytest.mark.django_db +def test_identity_add_valid_duplicate(client, person_foo, person_bar): + data = { + "type": PersonIdentity.IdentityType.OTHER, + "source": "Test source", + "value": "12345", + } + client.post( + reverse("person_identity-list", kwargs={"person_id": person_bar.id}), data=data + ) + + client.post( + reverse("person_identity-list", kwargs={"person_id": person_foo.id}), data=data + ) + + @pytest.mark.django_db def test_identity_delete(client, person_foo): response = client.get(