diff --git a/apps/opik-backend/src/main/java/com/comet/opik/utils/ValidationUtils.java b/apps/opik-backend/src/main/java/com/comet/opik/utils/ValidationUtils.java
index 40a0facf1b..b8d140e70b 100644
--- a/apps/opik-backend/src/main/java/com/comet/opik/utils/ValidationUtils.java
+++ b/apps/opik-backend/src/main/java/com/comet/opik/utils/ValidationUtils.java
@@ -7,6 +7,24 @@
public class ValidationUtils {
+ /**
+ * Regular expression to validate if a string is null or not blank.
+ *
+ *
It matches any string that is not null and contains at least one non-whitespace character.
+ * For example:
+ *
+ * - "" -> false
+ * - " " -> false
+ * - "\n" -> false
+ * - null -> true
+ * - "a" -> true
+ * - " a " -> true
+ * - "\n a \n" -> true
+ *
+ *
+ * @see Visual Explainer
+ * @see Ai Explainer
+ */
public static final String NULL_OR_NOT_BLANK = "(?s)^\\s*(\\S.*\\S|\\S)\\s*$";
/**
diff --git a/apps/opik-backend/src/test/java/com/comet/opik/utils/ValidationUtilsTest.java b/apps/opik-backend/src/test/java/com/comet/opik/utils/ValidationUtilsTest.java
new file mode 100644
index 0000000000..f0b2f978c8
--- /dev/null
+++ b/apps/opik-backend/src/test/java/com/comet/opik/utils/ValidationUtilsTest.java
@@ -0,0 +1,30 @@
+package com.comet.opik.utils;
+
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.stream.Stream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class ValidationUtilsTest {
+
+ public static Stream testNullOrNotBlank() {
+ return Stream.of(
+ Arguments.of("", false),
+ Arguments.of(" ", false),
+ Arguments.of("\n", false),
+ Arguments.of("a", true),
+ Arguments.of(" a ", true),
+ Arguments.of("\n a \n", true)
+ );
+ }
+
+ @ParameterizedTest
+ @MethodSource
+ void testNullOrNotBlank(String input, boolean expected) {
+ assertEquals(expected, input.matches(ValidationUtils.NULL_OR_NOT_BLANK));
+ }
+
+}
\ No newline at end of file