Remove "Generex" dependency 73/104973/26
authorYaroslav Lastivka <yaroslav.lastivka@pantheon.tech>
Tue, 21 Mar 2023 12:39:19 +0000 (14:39 +0200)
committerIvan Hrasko <ivan.hrasko@pantheon.tech>
Fri, 19 May 2023 13:40:57 +0000 (15:40 +0200)
Removed "Generex" and replaced with direct usage of "Automaton"
to generate random strings as "Genegex" seems to be no longer
actively developed project.

In fact the previous logic to generate example strings was broken.
We have fixed it by using RegularExpressionString instead of
JavaPatternString.

Now we have the same examples generated every run which helps us
to write better tests with assertions.

JIRA: NETCONF-980
Change-Id: I52e6d9d1a0f0cf43eb17b99309d9101c32d75a42
Signed-off-by: Yaroslav Lastivka <yaroslav.lastivka@pantheon.tech>
Signed-off-by: Ivan Hrasko <ivan.hrasko@pantheon.tech>
restconf/restconf-openapi/pom.xml
restconf/restconf-openapi/src/main/java/org/opendaylight/restconf/openapi/impl/DefinitionGenerator.java
restconf/restconf-openapi/src/test/java/org/opendaylight/restconf/openapi/impl/DefinitionGeneratorTest.java
restconf/restconf-openapi/src/test/java/org/opendaylight/restconf/openapi/mountpoints/MountPointOpenApiTest.java
restconf/restconf-openapi/src/test/resources/yang/strings-from-regex.yang [new file with mode: 0644]

index 0b8c8091056bf3a437a9d0e1d17bb14f17db9976..df4f0208fe75ebca320ba6c0b33f0c6910fceb74 100644 (file)
   <packaging>bundle</packaging>
 
   <dependencies>
-    <dependency>
-      <groupId>com.github.mifmif</groupId>
-      <artifactId>generex</artifactId>
-      <version>1.0.2</version>
-      <exclusions>
-        <exclusion>
-          <groupId>dk.brics.automaton</groupId>
-          <artifactId>automaton</artifactId>
-        </exclusion>
-      </exclusions>
-    </dependency>
     <dependency>
       <groupId>dk.brics</groupId>
       <artifactId>automaton</artifactId>
 
           <instructions>
             <Bundle-Name>Restconf OpenAPI Generator</Bundle-Name>
-            <Embed-Dependency>generex, automaton</Embed-Dependency>
+            <Embed-Dependency>automaton</Embed-Dependency>
             <Export-Package>
               com.fasterxml.jackson.datatype.*
             </Export-Package>
index 7a20c55894683b6b4de8a6cd44096462129c3738..0c101e4a84dcbd4db156fa2401b6d7699966488b 100644 (file)
@@ -23,7 +23,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
 import com.fasterxml.jackson.databind.node.TextNode;
 import com.google.common.collect.Range;
 import com.google.common.collect.RangeSet;
-import com.mifmif.common.regex.Generex;
+import dk.brics.automaton.RegExp;
 import java.io.IOException;
 import java.math.BigDecimal;
 import java.util.Collection;
@@ -122,9 +122,14 @@ public class DefinitionGenerator {
     private static final String INT32_FORMAT = "int32";
     private static final String INT64_FORMAT = "int64";
     private static final String BOOLEAN_TYPE = "boolean";
-    // Special characters used in automaton inside Generex.
+    // Special characters used in Automaton.
     // See https://www.brics.dk/automaton/doc/dk/brics/automaton/RegExp.html
     private static final Pattern AUTOMATON_SPECIAL_CHARACTERS = Pattern.compile("[@&\"<>#~]");
+    // Adaptation from YANG regex to Automaton regex
+    // See https://github.com/mifmif/Generex/blob/master/src/main/java/com/mifmif/common/regex/Generex.java
+    private static final Map<String, String> PREDEFINED_CHARACTER_CLASSES = Map.of("\\\\d", "[0-9]",
+            "\\\\D", "[^0-9]", "\\\\s", "[ \t\n\f\r]", "\\\\S", "[^ \t\n\f\r]",
+            "\\\\w", "[a-zA-Z_0-9]", "\\\\W", "[^a-zA-Z_0-9]");
 
     private Module topLevelModule;
 
@@ -797,14 +802,16 @@ public class DefinitionGenerator {
 
         if (type.getPatternConstraints().iterator().hasNext()) {
             final PatternConstraint pattern = type.getPatternConstraints().iterator().next();
-            String regex = pattern.getJavaPatternString();
-            regex = regex.substring(1, regex.length() - 1);
-            // Escape special characters to prevent issues inside Generex.
+            String regex = pattern.getRegularExpressionString();
+            // Escape special characters to prevent issues inside Automaton.
             regex = AUTOMATON_SPECIAL_CHARACTERS.matcher(regex).replaceAll("\\\\$0");
+            for (final var charClass : PREDEFINED_CHARACTER_CLASSES.entrySet()) {
+                regex = regex.replaceAll(charClass.getKey(), charClass.getValue());
+            }
             String defaultValue = "";
             try {
-                final Generex generex = new Generex(regex);
-                defaultValue = generex.random();
+                final RegExp regExp = new RegExp(regex);
+                defaultValue = regExp.toAutomaton().getShortestExample(true);
             } catch (IllegalArgumentException ex) {
                 LOG.warn("Cannot create example string for type: {} with regex: {}.", stringType.getQName(), regex);
             }
index 7eded3c4b07f493ac66daa308c0775fa86baffc7..12738bd2f2eb957f48e4cb40cf8172c398d93684 100644 (file)
@@ -7,6 +7,7 @@
  */
 package org.opendaylight.restconf.openapi.impl;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 
 import java.io.IOException;
@@ -38,4 +39,17 @@ public final class DefinitionGeneratorTest extends AbstractOpenApiTest {
         final var schemas = generator.convertToSchemas(module, CONTEXT, new DefinitionNames(), true);
         assertNotNull(schemas);
     }
+
+    @Test
+    public void testStringFromRegex() throws IOException {
+        final var module = CONTEXT.findModule("strings-from-regex").orElseThrow();
+        final var generator = new DefinitionGenerator();
+        final var jsonObject = generator.convertToSchemas(module, CONTEXT, new DefinitionNames(), true);
+        assertNotNull(jsonObject);
+
+        var properties = jsonObject.get("strings-from-regex_test").getProperties();
+        assertEquals("00:00:00:00:00:00", properties.get("mac-address").get("default").asText());
+        assertEquals("0000-00-00T00:00:00Z", properties.get("login-date-time").get("default").asText());
+        assertEquals("0.0.0.0", properties.get("ipv4-address").get("default").asText());
+    }
 }
index 9013984741ea8edabcf21126e7cb885135c28237..fa16f95a5ed6139fd2b5d187f5c18b5c1a84b0b6 100644 (file)
@@ -110,7 +110,7 @@ public final class MountPointOpenApiTest extends AbstractOpenApiTest {
         final Map<String, Path> paths = mountPointApi.getPaths();
         assertNotNull(paths);
 
-        assertEquals("Unexpected api list size", 26, paths.size());
+        assertEquals("Unexpected api list size", 27, paths.size());
 
         final List<JsonNode> getOperations = new ArrayList<>();
         final List<JsonNode> postOperations = new ArrayList<>();
@@ -126,10 +126,10 @@ public final class MountPointOpenApiTest extends AbstractOpenApiTest {
             Optional.ofNullable(path.getValue().getDelete()).ifPresent(deleteOperations::add);
         }
 
-        assertEquals("Unexpected GET paths size", 18, getOperations.size());
-        assertEquals("Unexpected POST paths size", 24, postOperations.size());
-        assertEquals("Unexpected PUT paths size", 16, putOperations.size());
-        assertEquals("Unexpected PATCH paths size", 16, patchOperations.size());
-        assertEquals("Unexpected DELETE paths size", 16, deleteOperations.size());
+        assertEquals("Unexpected GET paths size", 19, getOperations.size());
+        assertEquals("Unexpected POST paths size", 25, postOperations.size());
+        assertEquals("Unexpected PUT paths size", 17, putOperations.size());
+        assertEquals("Unexpected PATCH paths size", 17, patchOperations.size());
+        assertEquals("Unexpected DELETE paths size", 17, deleteOperations.size());
     }
 }
diff --git a/restconf/restconf-openapi/src/test/resources/yang/strings-from-regex.yang b/restconf/restconf-openapi/src/test/resources/yang/strings-from-regex.yang
new file mode 100644 (file)
index 0000000..849b4b9
--- /dev/null
@@ -0,0 +1,36 @@
+module strings-from-regex {
+  yang-version 1.1;
+  namespace "urn:ietf:params:xml:ns:yang:strings:regex";
+  prefix "str-rx";
+
+  typedef MacAddress {
+    type string {
+    pattern '[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}';
+    }
+  }
+
+  typedef Date-Time {
+    type string {
+      pattern '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[\+\-]\d{2}:\d{2})';
+    }
+  }
+
+  typedef ipv4-address {
+    type string {
+      pattern '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}'
+        + '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(%[\p{N}\p{L}]+)?';
+    }
+  }
+
+  container test {
+    leaf mac-address {
+      type MacAddress;
+    }
+    leaf login-date-time {
+      type Date-Time;
+    }
+    leaf ipv4-address {
+      type ipv4-address;
+    }
+  }
+}