From 3bdb1a1fade3280faa534bee7232986cb30f5aaa Mon Sep 17 00:00:00 2001
From: Erwan Rouchet <rouchet@teklia.com>
Date: Thu, 11 Jul 2024 15:59:53 +0200
Subject: [PATCH] Prevent empty WorkerConfiguration names

---
 .../management/commands/load_export.py        |  3 ++
 ...039_worker_configuration_name_not_empty.py | 47 +++++++++++++++++++
 arkindex/process/models.py                    |  9 +++-
 3 files changed, 57 insertions(+), 2 deletions(-)
 create mode 100644 arkindex/process/migrations/0039_worker_configuration_name_not_empty.py

diff --git a/arkindex/documents/management/commands/load_export.py b/arkindex/documents/management/commands/load_export.py
index 55ec306ee4..fcf94b6139 100644
--- a/arkindex/documents/management/commands/load_export.py
+++ b/arkindex/documents/management/commands/load_export.py
@@ -459,6 +459,9 @@ class Command(BaseCommand):
             configuration, _ = WorkerConfiguration.objects.get_or_create(
                 worker=Worker.objects.get(versions__id=worker_version_id),
                 configuration=json.loads(row["configuration"]),
+                # Configuration names are unique, but there are no configuration names in exports,
+                # so we use the closest unique field we have
+                defaults={"name": row["configuration_id"]},
             )
 
         return self.local_process.worker_runs.get_or_create(
diff --git a/arkindex/process/migrations/0039_worker_configuration_name_not_empty.py b/arkindex/process/migrations/0039_worker_configuration_name_not_empty.py
new file mode 100644
index 0000000000..fa85cdd989
--- /dev/null
+++ b/arkindex/process/migrations/0039_worker_configuration_name_not_empty.py
@@ -0,0 +1,47 @@
+# Generated by Django 5.0.6 on 2024-07-11 13:47
+
+from django.core.validators import MinLengthValidator
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ("process", "0038_remove_repository_worker_rights"),
+    ]
+
+    operations = [
+        # This does not make any change in the database, since validators are only Python-side
+        migrations.AlterField(
+            model_name="workerconfiguration",
+            name="name",
+            field=models.CharField(
+                max_length=250,
+                validators=[MinLengthValidator(1)],
+            ),
+        ),
+        # Give configurations with empty names some default name, "Configuration <n>"
+        migrations.RunSQL(
+            """
+            WITH to_update (id, name) AS (
+                SELECT id, 'Configuration ' || ROW_NUMBER() OVER (PARTITION BY worker_id ORDER BY id)
+                FROM process_workerconfiguration
+                WHERE name = ''
+            )
+            UPDATE process_workerconfiguration
+            SET name = to_update.name
+            FROM to_update
+            WHERE process_workerconfiguration.id = to_update.id
+            """,
+            reverse_sql=migrations.RunSQL.noop,
+            elidable=True,
+        ),
+        migrations.AddConstraint(
+            model_name="workerconfiguration",
+            constraint=models.CheckConstraint(
+                check=~models.Q(name=""),
+                name="worker_configuration_name_not_empty",
+                violation_error_message="Worker configuration name cannot be empty.",
+            ),
+        ),
+    ]
diff --git a/arkindex/process/models.py b/arkindex/process/models.py
index e8dd6c7f7b..bd963b51ac 100644
--- a/arkindex/process/models.py
+++ b/arkindex/process/models.py
@@ -819,7 +819,7 @@ class WorkerVersion(models.Model):
 
 
 class WorkerConfiguration(IndexableModel):
-    name = models.CharField(max_length=250)
+    name = models.CharField(max_length=250, validators=[MinLengthValidator(1)])
     configuration = models.JSONField(default=dict)
     configuration_hash = MD5HashField()
     archived = models.BooleanField(default=False)
@@ -844,7 +844,12 @@ class WorkerConfiguration(IndexableModel):
             models.CheckConstraint(
                 check=models.Q(configuration__typeof="object"),
                 name="worker_configuration_configuration_objects",
-            )
+            ),
+            models.CheckConstraint(
+                check=~Q(name=""),
+                name="worker_configuration_name_not_empty",
+                violation_error_message="Worker configuration name cannot be empty.",
+            ),
         ]
 
 
-- 
GitLab