This is an automated email from the ASF dual-hosted git repository.

yasithdev pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata.git


The following commit(s) were added to refs/heads/master by this push:
     new 6edb39c1a2 feat(research): short, human-readable experiment ids 
(EXP-XXXXXXXX) (#694)
6edb39c1a2 is described below

commit 6edb39c1a26fd2bcc9e454162a562811a03cd32a
Author: Yasith Jayawardana <[email protected]>
AuthorDate: Mon Jun 15 00:29:17 2026 -0400

    feat(research): short, human-readable experiment ids (EXP-XXXXXXXX) (#694)
    
    Experiment ids were sanitize(name)[:50] + "_" + UUID, e.g.
    Echo_on_Jun_14,_2026_9:36_PM_96aa3cbd-..., which mashed the name and a uuid 
into
    the primary key and the portal URL. Generate a short, URL-safe, 
collision-checked
    code instead (e.g. EXP-7QK2F9MX) via AiravataUtils.getReadableId, keeping 
the name
    as the separate experiment_name field. Only new experiments are affected; 
the id
    stays a varchar PK so existing ids and all FK references keep working. The 
portal
    already routes by experiment_id, so URLs become 
/workspace/experiments/EXP-7QK2F9MX/
    with no portal change.
    
    The two tests that asserted the id embeds the normalized name now assert 
the new
    contract: a short EXP- code, with the experiment name preserved verbatim.
---
 .../research/repository/ExperimentRepository.java    | 10 +++++++---
 .../ExperimentRepositoryIntegrationTest.java         | 20 ++++++++++++--------
 .../repository/ExperimentRepositoryTest.java         | 15 +++++++++++----
 .../java/org/apache/airavata/util/AiravataUtils.java | 20 ++++++++++++++++++++
 4 files changed, 50 insertions(+), 15 deletions(-)

diff --git 
a/airavata-api/research-service/src/main/java/org/apache/airavata/research/repository/ExperimentRepository.java
 
b/airavata-api/research-service/src/main/java/org/apache/airavata/research/repository/ExperimentRepository.java
index 50c46f2a9e..7d169cb035 100644
--- 
a/airavata-api/research-service/src/main/java/org/apache/airavata/research/repository/ExperimentRepository.java
+++ 
b/airavata-api/research-service/src/main/java/org/apache/airavata/research/repository/ExperimentRepository.java
@@ -164,11 +164,15 @@ public class ExperimentRepository extends 
AbstractRepository<ExperimentModel, Ex
                 .setState(ExperimentState.EXPERIMENT_STATE_CREATED)
                 
.setTimeOfStateChange(AiravataUtils.getCurrentTimestamp().getTime())
                 .build();
-        String expName = experimentModel.getExperimentName();
-        // This is to avoid overflow of experiment id size. Total experiment 
id length is <= 50 + UUID
+        // experiment_id is a short, human-readable, URL-safe code (e.g. 
EXP-7QK2F9MX). The experiment
+        // name is kept separately in experiment_name. Regenerate on the rare 
collision since it's the PK.
+        String experimentId;
+        do {
+            experimentId = AiravataUtils.getReadableId("EXP");
+        } while (isExperimentExist(experimentId));
         experimentModel = experimentModel.toBuilder()
                 .addExperimentStatus(experimentStatus)
-                .setExperimentId(AiravataUtils.getId(expName.substring(0, 
Math.min(expName.length(), 50))))
+                .setExperimentId(experimentId)
                 .build();
 
         return saveExperimentModelData(experimentModel);
diff --git 
a/airavata-api/research-service/src/test/java/org/apache/airavata/integration/ExperimentRepositoryIntegrationTest.java
 
b/airavata-api/research-service/src/test/java/org/apache/airavata/integration/ExperimentRepositoryIntegrationTest.java
index b6387b315f..d353108faa 100644
--- 
a/airavata-api/research-service/src/test/java/org/apache/airavata/integration/ExperimentRepositoryIntegrationTest.java
+++ 
b/airavata-api/research-service/src/test/java/org/apache/airavata/integration/ExperimentRepositoryIntegrationTest.java
@@ -210,20 +210,24 @@ public class ExperimentRepositoryIntegrationTest extends 
TestBase {
 
     @Test
     @Order(4)
-    @DisplayName("Slashes in experiment name are replaced with underscores in 
generated id")
-    void slashesInNameAreNormalized() throws Exception {
+    @DisplayName("experiment_id is a short readable code and the experiment 
name is preserved verbatim")
+    void experimentIdIsShortCodeAndNamePreserved() throws Exception {
         ExperimentModel experiment = buildExperiment("name/forward-slash//a");
         String experimentId = experimentRepository.addExperiment(experiment);
-        assertTrue(
-                experimentId.startsWith("name_forward-slash__a"),
-                "forward slashes should be replaced with underscores");
+        assertTrue(experimentId.startsWith("EXP-"), "experiment id should be a 
short EXP- code");
+        assertEquals(
+                "name/forward-slash//a",
+                
experimentRepository.getExperiment(experimentId).getExperimentName(),
+                "experiment name should be preserved verbatim");
         experimentRepository.removeExperiment(experimentId);
 
         experiment = buildExperiment("name\\backward-slash\\\\a");
         experimentId = experimentRepository.addExperiment(experiment);
-        assertTrue(
-                experimentId.startsWith("name_backward-slash__a"),
-                "backward slashes should be replaced with underscores");
+        assertTrue(experimentId.startsWith("EXP-"), "experiment id should be a 
short EXP- code");
+        assertEquals(
+                "name\\backward-slash\\\\a",
+                
experimentRepository.getExperiment(experimentId).getExperimentName(),
+                "experiment name should be preserved verbatim");
         experimentRepository.removeExperiment(experimentId);
     }
 }
diff --git 
a/airavata-api/research-service/src/test/java/org/apache/airavata/research/repository/ExperimentRepositoryTest.java
 
b/airavata-api/research-service/src/test/java/org/apache/airavata/research/repository/ExperimentRepositoryTest.java
index 81243f8bde..ea5ea8f6d6 100644
--- 
a/airavata-api/research-service/src/test/java/org/apache/airavata/research/repository/ExperimentRepositoryTest.java
+++ 
b/airavata-api/research-service/src/test/java/org/apache/airavata/research/repository/ExperimentRepositoryTest.java
@@ -306,10 +306,11 @@ public class ExperimentRepositoryTest extends TestBase {
     }
 
     /**
-     * Verify that slashes (forward and backward) are replaced with 
underscores.
+     * The generated experiment id is a short, readable code (independent of 
the name), and the
+     * experiment name is preserved verbatim (slashes are no longer mangled 
into the id).
      */
     @Test
-    public void testSlashesInExperimentName() throws RegistryException {
+    public void testGeneratedExperimentIdAndNamePreservation() throws 
RegistryException {
 
         // Forward slashes
         ExperimentModel experimentModel =
@@ -327,7 +328,10 @@ public class ExperimentRepositoryTest extends TestBase {
                 .build();
 
         String experimentId = 
experimentRepository.addExperiment(experimentModel);
-        assertTrue(experimentId.startsWith("name_forward-slash__a"));
+        assertTrue(experimentId.startsWith("EXP-"));
+        assertEquals(
+                "name/forward-slash//a",
+                
experimentRepository.getExperiment(experimentId).getExperimentName());
 
         // Backward slashes
         experimentModel = 
ExperimentModel.newBuilder().setProjectId(projectId).build();
@@ -344,6 +348,9 @@ public class ExperimentRepositoryTest extends TestBase {
                 .build();
 
         experimentId = experimentRepository.addExperiment(experimentModel);
-        assertTrue(experimentId.startsWith("name_backward-slash__a"));
+        assertTrue(experimentId.startsWith("EXP-"));
+        assertEquals(
+                "name\\backward-slash\\\\a",
+                
experimentRepository.getExperiment(experimentId).getExperimentName());
     }
 }
diff --git 
a/airavata-api/src/main/java/org/apache/airavata/util/AiravataUtils.java 
b/airavata-api/src/main/java/org/apache/airavata/util/AiravataUtils.java
index 1f6631fe74..0a3dea54d8 100644
--- a/airavata-api/src/main/java/org/apache/airavata/util/AiravataUtils.java
+++ b/airavata-api/src/main/java/org/apache/airavata/util/AiravataUtils.java
@@ -19,12 +19,19 @@
 */
 package org.apache.airavata.util;
 
+import java.security.SecureRandom;
 import java.sql.Timestamp;
 import java.util.Calendar;
 import java.util.UUID;
 
 public class AiravataUtils {
 
+    // Unambiguous Crockford-style base32 alphabet (no 0/1/I/L/O) for short, 
human-readable,
+    // URL-safe ids.
+    private static final char[] READABLE_ID_ALPHABET = 
"23456789ABCDEFGHJKLMNPQRSTUVWXYZ".toCharArray();
+    private static final int READABLE_ID_LENGTH = 8;
+    private static final SecureRandom READABLE_ID_RANDOM = new SecureRandom();
+
     public static Timestamp getCurrentTimestamp() {
         Calendar calender = Calendar.getInstance();
         //        java.util.Date d = calender.getTimeInMillis();
@@ -42,4 +49,17 @@ public class AiravataUtils {
         String id = name.trim().replaceAll("\\s|\\.|/|\\\\", "_");
         return id + "_" + UUID.randomUUID();
     }
+
+    /**
+     * Generate a short, human-readable, URL-safe id of the form {@code 
<prefix>-XXXXXXXX} using an
+     * unambiguous alphabet (no 0/1/I/L/O). The id carries no semantic 
meaning; callers that use it as
+     * a primary key should regenerate it on the (vanishingly rare) collision.
+     */
+    public static String getReadableId(String prefix) {
+        StringBuilder sb = new StringBuilder(prefix).append('-');
+        for (int i = 0; i < READABLE_ID_LENGTH; i++) {
+            
sb.append(READABLE_ID_ALPHABET[READABLE_ID_RANDOM.nextInt(READABLE_ID_ALPHABET.length)]);
+        }
+        return sb.toString();
+    }
 }

Reply via email to