diff --git a/arkindex/documents/serializers/elements.py b/arkindex/documents/serializers/elements.py index 19a09447bb718528eef9eebbbcc569faaf5a1ec2..776711a5de9aea1bef0641e5670207be84d902da 100644 --- a/arkindex/documents/serializers/elements.py +++ b/arkindex/documents/serializers/elements.py @@ -172,6 +172,32 @@ class MetaDataBulkSerializer(serializers.Serializer): super().__init__(*args, **kwargs) self.fields['metadata_list'] = MetaDataBulkItemSerializer(many=True, allow_empty=False, context=self.context) + def make_metadata_tuples(self, meta_list): + tuples_list = [] + for item in meta_list: + item_tuple = ( + item.get('name'), + item.get('type').value, + item.get('value') + ) + tuples_list.append(item_tuple) + return tuples_list + + def validate(self, data): + data = super().validate(data) + request_metadata = self.make_metadata_tuples(data['metadata_list']) + unique_metadata = set(request_metadata) + if len(unique_metadata) != len(request_metadata): + raise ValidationError('Metadata in metadata list must make a unique set.') + names = [item['name'] for item in data['metadata_list']] + types = [item['type'] for item in data['metadata_list']] + filtered_metadata = MetaData.objects.filter(element=self.context['element'].id, name__in=names, type__in=types).values() + existing_metadata = self.make_metadata_tuples(filtered_metadata) + meta_matches = set(request_metadata) & set(existing_metadata) + if meta_matches: + raise ValidationError(f'Metadata {meta_matches} already exist(s) on this element.') + return data + def create(self, validated_data): validated_data['metadata_list'] = MetaData.objects.bulk_create([ MetaData( diff --git a/arkindex/documents/tests/test_metadata.py b/arkindex/documents/tests/test_metadata.py index 248dc5efb6ddafa5a448d5b17acf3d356085ef4a..180ee8ccc11f6680ff657e90000c34b94d6a96ed 100644 --- a/arkindex/documents/tests/test_metadata.py +++ b/arkindex/documents/tests/test_metadata.py @@ -967,3 +967,45 @@ class TestMetaData(FixtureAPITestCase): {'name': 'not allowed', 'type': MetaType.Location} ] ) + + def test_bulk_create_metadata_unique(self): + """ + The metadata created with the bulk endpoint must be unique + """ + self.client.force_login(self.user) + with self.assertNumQueries(5): + response = self.client.post( + reverse('api:element-metadata-bulk', kwargs={'pk': str(self.vol.id)}), + data={'metadata_list': [ + {'type': 'location', 'name': 'location', 'value': 'Texas'}, + {'type': 'location', 'name': 'location', 'value': 'Texas'} + ]}, + format='json' + ) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertDictEqual(response.json(), { + 'non_field_errors': ['Metadata in metadata list must make a unique set.'] + }) + + def test_bulk_create_metadata_no_duplicates(self): + """ + Do not create metadata that already exist on the element + """ + self.client.force_login(self.user) + response_1 = self.client.post( + reverse('api:element-metadata', kwargs={'pk': str(self.vol.id)}), + data={'type': 'location', 'name': 'location', 'value': 'Texas'} + ) + self.assertEqual(response_1.status_code, status.HTTP_201_CREATED) + with self.assertNumQueries(4): + response_2 = self.client.post( + reverse('api:element-metadata-bulk', kwargs={'pk': str(self.vol.id)}), + data={'metadata_list': [ + {'type': 'location', 'name': 'location', 'value': 'Texas'} + ]}, + format='json' + ) + self.assertEqual(response_2.status_code, status.HTTP_400_BAD_REQUEST) + self.assertDictEqual(response_2.json(), { + 'non_field_errors': ["Metadata {('location', 'location', 'Texas')} already exist(s) on this element."] + })