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

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


The following commit(s) were added to refs/heads/master by this push:
     new 792ef9797 [type:feature] added expression and replaced some grammar of 
${} with expression  (#4180)
792ef9797 is described below

commit 792ef97972c00d122a2e13ef17511eae24d11f7b
Author: 愿凌飞 <[email protected]>
AuthorDate: Mon Nov 28 15:42:37 2022 +0800

    [type:feature] added expression and replaced some grammar of ${} with 
expression  (#4180)
    
    * Added mock ${} support SpEL
    
    * implement first idea
    
    * revert old generator
    
    * removed redundancy
    
    * handled exceptions uniformly and added test
    
    * fixed code style
    
    Co-authored-by: xiaoyu <[email protected]>
    Co-authored-by: likeguo <[email protected]>
---
 .../test/http/combination/MockPluginTest.java      |   2 +-
 .../plugin/mock/generator/BoolGenerator.java       |  12 +-
 .../mock/generator/CurrentTimeGenerator.java       |  23 +--
 .../plugin/mock/generator/EmailGenerator.java      |  21 +--
 .../plugin/mock/generator/EnStringGenerator.java   |  21 ++-
 .../plugin/mock/generator/ExpressionGenerator.java | 109 ++++++++++++
 .../plugin/mock/generator/GeneratorFactory.java    |  42 +++--
 .../plugin/mock/generator/PhoneGenerator.java      |  19 +--
 .../mock/generator/RandomDoubleGenerator.java      |  25 ++-
 .../plugin/mock/generator/RandomIntGenerator.java  |  20 +--
 .../plugin/mock/generator/RangeDataGenerator.java  |  23 ++-
 .../plugin/mock/generator/ZhStringGenerator.java   |  23 ++-
 .../apache/shenyu/plugin/mock/util/MockUtil.java   | 184 +++++++++++++++++++++
 ...g.apache.shenyu.plugin.mock.generator.Generator |   1 +
 .../plugin/mock/generator/ArrayGeneratorTest.java  |   8 +-
 .../mock/generator/ExpressionGeneratorTest.java    | 137 +++++++++++++++
 .../mock/generator/GeneratorFactoryTest.java}      |  44 +++--
 17 files changed, 556 insertions(+), 158 deletions(-)

diff --git 
a/shenyu-integrated-test/shenyu-integrated-test-http/src/test/java/org/apache/shenyu/integrated/test/http/combination/MockPluginTest.java
 
b/shenyu-integrated-test/shenyu-integrated-test-http/src/test/java/org/apache/shenyu/integrated/test/http/combination/MockPluginTest.java
index 79df74178..d39fac765 100644
--- 
a/shenyu-integrated-test/shenyu-integrated-test-http/src/test/java/org/apache/shenyu/integrated/test/http/combination/MockPluginTest.java
+++ 
b/shenyu-integrated-test/shenyu-integrated-test-http/src/test/java/org/apache/shenyu/integrated/test/http/combination/MockPluginTest.java
@@ -100,7 +100,7 @@ public class MockPluginTest extends AbstractPluginDataInit {
         
         MockHandle placeholderMockHandler = new MockHandle();
         placeholderMockHandler.setHttpStatusCode(200);
-        placeholderMockHandler.setResponseContent("{\"number\":${int|10-20}}");
+        
placeholderMockHandler.setResponseContent("{\"number\":${expression|#int(10,20)}");
         ruleLocalDataList.add(buildRuleLocalData(TEST_PLACEHOLDER_MOCK, 
placeholderMockHandler));
         
         return ruleLocalDataList;
diff --git 
a/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/generator/BoolGenerator.java
 
b/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/generator/BoolGenerator.java
index 1233bccdb..5149e9f05 100644
--- 
a/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/generator/BoolGenerator.java
+++ 
b/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/generator/BoolGenerator.java
@@ -17,7 +17,7 @@
 
 package org.apache.shenyu.plugin.mock.generator;
 
-import org.apache.shenyu.plugin.mock.util.RandomUtil;
+import org.apache.shenyu.plugin.mock.util.MockUtil;
 import org.apache.shenyu.spi.Join;
 
 /**
@@ -25,22 +25,22 @@ import org.apache.shenyu.spi.Join;
  */
 @Join
 public class BoolGenerator implements Generator<Boolean> {
-    
+
     @Override
     public String getName() {
         return "bool";
     }
-    
+
     @Override
     public Boolean generate() {
-        return RandomUtil.randomInt(0, 1) == 1;
+        return MockUtil.bool();
     }
-    
+
     @Override
     public int getParamSize() {
         return 0;
     }
-    
+
     @Override
     public boolean match(final String rule) {
         return rule.matches("^bool$");
diff --git 
a/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/generator/CurrentTimeGenerator.java
 
b/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/generator/CurrentTimeGenerator.java
index 372c6d71b..bba44f180 100644
--- 
a/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/generator/CurrentTimeGenerator.java
+++ 
b/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/generator/CurrentTimeGenerator.java
@@ -17,13 +17,9 @@
 
 package org.apache.shenyu.plugin.mock.generator;
 
+import org.apache.shenyu.plugin.mock.util.MockUtil;
 import org.apache.shenyu.spi.Join;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
-import java.time.DateTimeException;
-import java.time.LocalDateTime;
-import java.time.format.DateTimeFormatter;
 import java.util.List;
 
 /**
@@ -31,11 +27,7 @@ import java.util.List;
  */
 @Join
 public class CurrentTimeGenerator implements Generator<String> {
-    
-    private static final String DEFAULT_FORMAT = "YYYY-MM-dd HH:mm:ss";
-    
-    private static final Logger LOG = 
LoggerFactory.getLogger(CurrentTimeGenerator.class);
-    
+
     private String format;
     
     @Override
@@ -45,14 +37,7 @@ public class CurrentTimeGenerator implements 
Generator<String> {
     
     @Override
     public String generate() {
-        LocalDateTime now = LocalDateTime.now();
-        try {
-            return DateTimeFormatter.ofPattern(format).format(now);
-        } catch (DateTimeException e) {
-            LOG.warn("format fail,use default format :{}", DEFAULT_FORMAT);
-            return DateTimeFormatter.ofPattern(DEFAULT_FORMAT).format(now);
-        }
-        
+        return MockUtil.current(format);
     }
     
     @Override
@@ -64,8 +49,6 @@ public class CurrentTimeGenerator implements 
Generator<String> {
     public void initParam(final List<String> params, final String rule) {
         if (params.size() >= 1) {
             format = params.get(0);
-        } else {
-            format = DEFAULT_FORMAT;
         }
     }
     
diff --git 
a/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/generator/EmailGenerator.java
 
b/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/generator/EmailGenerator.java
index 9e064ebf2..4e3ea7513 100644
--- 
a/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/generator/EmailGenerator.java
+++ 
b/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/generator/EmailGenerator.java
@@ -17,42 +17,35 @@
 
 package org.apache.shenyu.plugin.mock.generator;
 
+import org.apache.shenyu.plugin.mock.util.MockUtil;
 import org.apache.shenyu.spi.Join;
 
-import static org.apache.shenyu.plugin.mock.util.RandomUtil.randomInt;
-import static 
org.apache.shenyu.plugin.mock.util.RandomUtil.randomLowerLetterString;
-
 /**
  * Random email address generator.
  */
 @Join
 public class EmailGenerator implements Generator<String> {
-    
-    private static final String[] DOMAIN_SUFFIX = {"com", "org", "cn", 
"com.cn", "top", "edu", "io"};
-    
+
     @Override
     public String getName() {
         return "email";
     }
-    
+
     @Override
     public String generate() {
-        return String.format("%s@%s.%s",
-            randomLowerLetterString(randomInt(5, 10)),
-            randomLowerLetterString(randomInt(3, 8)),
-            DOMAIN_SUFFIX[randomInt(0, DOMAIN_SUFFIX.length - 1)]);
+        return MockUtil.email();
     }
-    
+
     @Override
     public int getParamSize() {
         return 0;
     }
-    
+
     @Override
     public boolean match(final String rule) {
         return rule.matches("^email$");
     }
-    
+
     @Override
     public String[] getPrefixAndSuffix() {
         return new String[]{"\"", "\""};
diff --git 
a/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/generator/EnStringGenerator.java
 
b/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/generator/EnStringGenerator.java
index d52e3a0d0..1dc7bcdab 100644
--- 
a/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/generator/EnStringGenerator.java
+++ 
b/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/generator/EnStringGenerator.java
@@ -17,8 +17,7 @@
 
 package org.apache.shenyu.plugin.mock.generator;
 
-import org.apache.commons.lang3.RandomStringUtils;
-import org.apache.shenyu.plugin.mock.util.RandomUtil;
+import org.apache.shenyu.plugin.mock.util.MockUtil;
 import org.apache.shenyu.spi.Join;
 
 import java.util.List;
@@ -28,38 +27,38 @@ import java.util.List;
  */
 @Join
 public class EnStringGenerator implements Generator<String> {
-    
+
     private int min;
-    
+
     private int max;
-    
+
     @Override
     public String getName() {
         return "zh";
     }
-    
+
     @Override
     public String generate() {
-        return RandomStringUtils.random(RandomUtil.randomInt(min, max), 5, 
129, true, false);
+        return MockUtil.en(min, max);
     }
-    
+
     @Override
     public int getParamSize() {
         return 1;
     }
-    
+
     @Override
     public void initParam(final List<String> params, final String rule) {
         String[] range = params.get(0).split("-");
         min = Integer.parseInt(range[0]);
         max = Integer.parseInt(range[1]);
     }
-    
+
     @Override
     public boolean match(final String rule) {
         return rule.matches("^en\\|\\d+-\\d+$");
     }
-    
+
     @Override
     public String[] getPrefixAndSuffix() {
         return new String[]{"\"", "\""};
diff --git 
a/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/generator/ExpressionGenerator.java
 
b/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/generator/ExpressionGenerator.java
new file mode 100644
index 000000000..8cdcc5364
--- /dev/null
+++ 
b/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/generator/ExpressionGenerator.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.plugin.mock.generator;
+
+import org.apache.shenyu.common.utils.JsonUtils;
+import org.apache.shenyu.plugin.mock.util.MockUtil;
+import org.apache.shenyu.spi.Join;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.expression.EvaluationContext;
+import org.springframework.expression.ExpressionParser;
+import org.springframework.expression.spel.standard.SpelExpressionParser;
+import org.springframework.expression.spel.support.StandardEvaluationContext;
+
+import java.util.List;
+
+@Join
+public class ExpressionGenerator implements Generator<String> {
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(ExpressionGenerator.class);
+
+    private static final ExpressionParser PARSER = new SpelExpressionParser();
+
+    private static final EvaluationContext CONTEXT = initContext();
+
+    private String expression;
+
+    @Override
+    public String getName() {
+        return "expression";
+    }
+
+    @Override
+    public String generate() {
+        Object val = PARSER.parseExpression(expression).getValue(CONTEXT);
+        if (val instanceof MockUtil.FormatDouble) {
+            return val.toString();
+        }
+        return JsonUtils.toJson(val);
+    }
+
+    @Override
+    public int getParamSize() {
+        return 1;
+    }
+
+    @Override
+    public void initParam(final List<String> params, final String rule) {
+        expression = params.get(0);
+    }
+
+    @Override
+    public boolean match(final String rule) {
+        return rule.matches("^expression\\|.+");
+    }
+
+    private static EvaluationContext initContext() {
+
+        StandardEvaluationContext context = new StandardEvaluationContext();
+
+        try {
+            registerMockFunction(context, "double", "randomDouble", 
double.class, double.class, String[].class);
+
+            registerMockFunction(context, "bool", "bool");
+
+            registerMockFunction(context, "int", "randomInt", int.class, 
int.class);
+
+            registerMockFunction(context, "email", "email");
+
+            registerMockFunction(context, "phone", "phone");
+
+            registerMockFunction(context, "zh", "zh", int.class, int.class);
+
+            registerMockFunction(context, "en", "en", int.class, int.class);
+
+            registerMockFunction(context, "oneOf", "oneOf", Object[].class);
+
+            registerMockFunction(context, "current", "current", 
String[].class);
+
+        } catch (NoSuchMethodException e) {
+            // It will never happen
+            LOG.error(e.getMessage(), e);
+        }
+        return context;
+    }
+
+    private static void registerMockFunction(final StandardEvaluationContext 
context,
+                                             final String name,
+                                             final String methodName,
+                                             final Class<?>... parameterTypes) 
throws NoSuchMethodException {
+        context.registerFunction(name,
+                MockUtil.class.getDeclaredMethod(methodName, parameterTypes));
+    }
+}
diff --git 
a/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/generator/GeneratorFactory.java
 
b/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/generator/GeneratorFactory.java
index ce02e458b..45154d9b2 100644
--- 
a/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/generator/GeneratorFactory.java
+++ 
b/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/generator/GeneratorFactory.java
@@ -29,24 +29,24 @@ import java.util.regex.Pattern;
  * GeneratorFactory.
  */
 public final class GeneratorFactory {
-    
+
     private static final Logger LOG = 
LoggerFactory.getLogger(GeneratorFactory.class);
-    
+
     /**
      * If expression parsing fails, the ${} placeholder
      * will be replaced with the following.
      */
     private static final String ERROR_PARSE = "\"[#ERROR EXPRESSION#]\"";
-    
+
     /**
      * Regular expression to extract rule content.
      */
     private static final Pattern RULE_CONTENT_PATTERN = Pattern
             .compile("^\\$\\{(.+?)}$", Pattern.MULTILINE);
-    
+
     private GeneratorFactory() {
     }
-    
+
     /**
      * New instance mock data generator.
      *
@@ -62,25 +62,31 @@ public final class GeneratorFactory {
         }
         return null;
     }
-    
+
     private static String generate(final String rule) {
         final Matcher matcher = RULE_CONTENT_PATTERN.matcher(rule);
-        if (matcher.find()) {
-            String ruleContent = matcher.group(1);
-            String ruleName = ruleContent.split("\\|")[0];
-            Generator<?> generator = newInstance(ruleName, rule);
-            if (generator == null || !generator.match(ruleContent)) {
-                return rule;
-            }
-            String[] prefixAndSuffix = generator.getPrefixAndSuffix();
+        if (!matcher.find()) {
+            return rule;
+        }
+
+        String ruleContent = matcher.group(1);
+        String ruleName = ruleContent.split("\\|")[0];
+        Generator<?> generator = newInstance(ruleName, rule);
+        if (generator == null || !generator.match(ruleContent)) {
+            return rule;
+        }
+
+        String[] prefixAndSuffix = generator.getPrefixAndSuffix();
+        try {
             generator.parseRule(ruleContent);
             Object generateData = generator.generate();
             return String.join("", prefixAndSuffix[0], 
generateData.toString(), prefixAndSuffix[1]);
-        } else {
+        } catch (Exception e) {
+            LOG.error(e.getMessage(), e);
             return rule;
         }
     }
-    
+
     /**
      * replace placeholder in content.
      *
@@ -96,13 +102,13 @@ public final class GeneratorFactory {
                 generateData = ERROR_PARSE;
             }
             String toString = String.valueOf(generateData);
-            placeHolder = placeHolder.replaceAll("([$|{}\\]\\[])", "\\\\$1");
+            placeHolder = placeHolder.replaceAll("([$|{}()\\]\\[])", "\\\\$1");
             afterDeal = afterDeal.replaceFirst(placeHolder, toString);
             placeHolder = getPlaceholder(afterDeal);
         }
         return afterDeal;
     }
-    
+
     private static String getPlaceholder(final String rule) {
         int start = rule.indexOf("${");
         if (start < 0) {
diff --git 
a/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/generator/PhoneGenerator.java
 
b/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/generator/PhoneGenerator.java
index 3008254ec..4edcd1f30 100644
--- 
a/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/generator/PhoneGenerator.java
+++ 
b/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/generator/PhoneGenerator.java
@@ -17,7 +17,7 @@
 
 package org.apache.shenyu.plugin.mock.generator;
 
-import org.apache.shenyu.plugin.mock.util.RandomUtil;
+import org.apache.shenyu.plugin.mock.util.MockUtil;
 import org.apache.shenyu.spi.Join;
 
 /**
@@ -25,32 +25,27 @@ import org.apache.shenyu.spi.Join;
  */
 @Join
 public class PhoneGenerator implements Generator<String> {
-    
+
     @Override
     public String getName() {
         return "phone";
     }
-    
+
     @Override
     public String generate() {
-        StringBuilder builder = new StringBuilder("1");
-        builder.append(RandomUtil.randomInt(3, 9));
-        for (int i = 0; i < 9; i++) {
-            builder.append(RandomUtil.randomInt(0, 9));
-        }
-        return builder.toString();
+        return MockUtil.phone();
     }
-    
+
     @Override
     public int getParamSize() {
         return 0;
     }
-    
+
     @Override
     public boolean match(final String rule) {
         return rule.matches("^phone$");
     }
-    
+
     @Override
     public String[] getPrefixAndSuffix() {
         return new String[]{"\"", "\""};
diff --git 
a/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/generator/RandomDoubleGenerator.java
 
b/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/generator/RandomDoubleGenerator.java
index 859f07574..82e4fe6b5 100644
--- 
a/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/generator/RandomDoubleGenerator.java
+++ 
b/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/generator/RandomDoubleGenerator.java
@@ -17,6 +17,7 @@
 
 package org.apache.shenyu.plugin.mock.generator;
 
+import org.apache.shenyu.plugin.mock.util.MockUtil;
 import org.apache.shenyu.spi.Join;
 
 import java.util.List;
@@ -27,32 +28,28 @@ import java.util.Objects;
  */
 @Join
 public class RandomDoubleGenerator implements Generator<String> {
-    
+
     private Double min;
-    
+
     private Double max;
-    
+
     private String format;
-    
+
     @Override
     public String getName() {
         return "double";
     }
-    
+
     @Override
     public String generate() {
-        Double result = (Math.random() * (max - min)) + min;
-        if (format != null) {
-            return String.format(format, result);
-        }
-        return String.valueOf(result);
+        return MockUtil.randomDouble(min, max, format).toString();
     }
-    
+
     @Override
     public int getParamSize() {
         return 1;
     }
-    
+
     @Override
     public void initParam(final List<String> params, final String rule) {
         String[] range = params.get(0).split("-");
@@ -62,12 +59,12 @@ public class RandomDoubleGenerator implements 
Generator<String> {
             format = Objects.equals(params.get(1), "") ? null : params.get(1);
         }
     }
-    
+
     @Override
     public boolean match(final String rule) {
         return rule.matches("^double\\|\\d+(?:\\.\\d+)?-\\d+(?:\\.\\d+)?.*");
     }
-    
+
     @Override
     public String[] getPrefixAndSuffix() {
         return new String[]{"\"", "\""};
diff --git 
a/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/generator/RandomIntGenerator.java
 
b/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/generator/RandomIntGenerator.java
index 3defb223e..d48502c42 100644
--- 
a/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/generator/RandomIntGenerator.java
+++ 
b/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/generator/RandomIntGenerator.java
@@ -17,7 +17,7 @@
 
 package org.apache.shenyu.plugin.mock.generator;
 
-import org.apache.shenyu.plugin.mock.util.RandomUtil;
+import org.apache.shenyu.plugin.mock.util.MockUtil;
 import org.apache.shenyu.spi.Join;
 
 import java.util.List;
@@ -27,36 +27,36 @@ import java.util.List;
  */
 @Join
 public class RandomIntGenerator implements Generator<Integer> {
-    
+
     private int min;
-    
+
     private int max;
-    
+
     @Override
     public String getName() {
         return "int";
     }
-    
+
     @Override
     public Integer generate() {
-        return RandomUtil.randomInt(min, max);
+        return MockUtil.randomInt(min, max);
     }
-    
+
     @Override
     public int getParamSize() {
         return 1;
     }
-    
+
     @Override
     public void initParam(final List<String> params, final String rule) {
         String[] range = params.get(0).split("-");
         min = Integer.parseInt(range[0]);
         max = Integer.parseInt(range[1]);
     }
-    
+
     @Override
     public boolean match(final String rule) {
         return rule.matches("^int\\|\\d+-\\d+$");
     }
-    
+
 }
diff --git 
a/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/generator/RangeDataGenerator.java
 
b/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/generator/RangeDataGenerator.java
index 3e957c9b9..60cbbbb09 100644
--- 
a/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/generator/RangeDataGenerator.java
+++ 
b/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/generator/RangeDataGenerator.java
@@ -17,44 +17,43 @@
 
 package org.apache.shenyu.plugin.mock.generator;
 
-import org.apache.shenyu.plugin.mock.util.RandomUtil;
+import org.apache.shenyu.plugin.mock.util.MockUtil;
 import org.apache.shenyu.spi.Join;
 
 import java.util.Arrays;
 import java.util.List;
-import java.util.stream.Collectors;
 
 /**
  * Specify the in-list data generator.
  */
 @Join
 public class RangeDataGenerator implements Generator<String> {
-    
-    private List<String> data;
-    
+
+    private String[] data;
+
     @Override
     public String getName() {
         return "list";
     }
-    
+
     @Override
     public String generate() {
-        return data.get(RandomUtil.randomInt(0, data.size() - 1));
+        return MockUtil.oneOf(data).toString();
     }
-    
+
     @Override
     public int getParamSize() {
         return 0;
     }
-    
+
     @Override
     public void initParam(final List<String> params, final String rule) {
         String rangeData = params.get(0).replaceAll("\\[(.+)]", "$1");
         data = Arrays.stream(rangeData.split("(?<!\\\\),"))
                 .map(data -> data.replace("\\,", ","))
-                .collect(Collectors.toList());
+                .toArray(String[]::new);
     }
-    
+
     @Override
     public boolean match(final String rule) {
         boolean matches = rule.matches("^list\\|\\[.+]$");
@@ -64,7 +63,7 @@ public class RangeDataGenerator implements Generator<String> {
         }
         return false;
     }
-    
+
     @Override
     public String[] getPrefixAndSuffix() {
         return new String[]{"\"", "\""};
diff --git 
a/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/generator/ZhStringGenerator.java
 
b/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/generator/ZhStringGenerator.java
index 996484740..9f6e00668 100644
--- 
a/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/generator/ZhStringGenerator.java
+++ 
b/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/generator/ZhStringGenerator.java
@@ -17,51 +17,48 @@
 
 package org.apache.shenyu.plugin.mock.generator;
 
-import org.apache.commons.lang3.RandomStringUtils;
+import org.apache.shenyu.plugin.mock.util.MockUtil;
 import org.apache.shenyu.spi.Join;
 
 import java.util.List;
-import java.util.Random;
 
 /**
  * Random length Chinese string generator.
  */
 @Join
 public class ZhStringGenerator implements Generator<String> {
-    
+
     private int min;
-    
+
     private int max;
-    
+
     @Override
     public String getName() {
         return "zh";
     }
-    
+
     @Override
     public String generate() {
-        Random random = new Random();
-        int len = random.nextInt(max - min - 1) + min;
-        return RandomStringUtils.random(len, 0x4e00, 0x9fa5, false, false);
+        return MockUtil.zh(min, max);
     }
-    
+
     @Override
     public int getParamSize() {
         return 1;
     }
-    
+
     @Override
     public void initParam(final List<String> params, final String rule) {
         String[] range = params.get(0).split("-");
         min = Integer.parseInt(range[0]);
         max = Integer.parseInt(range[1]);
     }
-    
+
     @Override
     public boolean match(final String rule) {
         return rule.matches("^zh\\|\\d+-\\d+$");
     }
-    
+
     @Override
     public String[] getPrefixAndSuffix() {
         return new String[]{"\"", "\""};
diff --git 
a/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/util/MockUtil.java
 
b/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/util/MockUtil.java
new file mode 100644
index 000000000..083be1e92
--- /dev/null
+++ 
b/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/util/MockUtil.java
@@ -0,0 +1,184 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.plugin.mock.util;
+
+import org.apache.commons.lang3.RandomStringUtils;
+import org.apache.shenyu.plugin.mock.generator.CurrentTimeGenerator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.time.DateTimeException;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Objects;
+import java.util.Random;
+
+import static 
org.apache.shenyu.plugin.mock.util.RandomUtil.randomLowerLetterString;
+
+public class MockUtil {
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(CurrentTimeGenerator.class);
+
+    private static final String DEFAULT_FORMAT = "YYYY-MM-dd HH:mm:ss";
+
+    private static final String[] DOMAIN_SUFFIX = {"com", "org", "cn", 
"com.cn", "top", "edu", "io"};
+
+    /**
+     * Randomly generate Boolean.
+     *
+     * @return Boolean
+     */
+    public static Boolean bool() {
+        return RandomUtil.randomInt(0, 1) == 1;
+    }
+
+    /**
+     * Randomly generate int in the specified range.
+     *
+     * @param min min
+     * @param max max
+     * @return int
+     */
+    public static int randomInt(final int min, final int max) {
+        return RandomUtil.randomInt(min, max);
+    }
+
+    /**
+     * Randomly generate Double in the specified range.
+     *
+     * @param min    min
+     * @param max    max
+     * @param format format
+     * @return formatDouble
+     */
+    public static FormatDouble randomDouble(final double min, final double 
max, final String... format) {
+
+        Double result = (Math.random() * (max - min)) + min;
+        if (format != null && format.length != 0) {
+            return new FormatDouble(result, format[0]);
+        }
+        return new FormatDouble(result);
+    }
+
+    /**
+     * Randomly generate email.
+     *
+     * @return email
+     */
+    public static String email() {
+        return String.format("%s@%s.%s",
+                randomLowerLetterString(randomInt(5, 10)),
+                randomLowerLetterString(randomInt(3, 8)),
+                DOMAIN_SUFFIX[randomInt(0, DOMAIN_SUFFIX.length - 1)]);
+    }
+
+    /**
+     * Randomly generate phone.
+     *
+     * @return phone
+     */
+    public static String phone() {
+        StringBuilder builder = new StringBuilder("1");
+        builder.append(RandomUtil.randomInt(3, 9));
+        for (int i = 0; i < 9; i++) {
+            builder.append(RandomUtil.randomInt(0, 9));
+        }
+        return builder.toString();
+    }
+
+    /**
+     * Randomly generate Chinese string.
+     *
+     * @param min min
+     * @param max max
+     * @return chinese string
+     */
+    public static String zh(final int min, final int max) {
+        Random random = new Random();
+        int len = random.nextInt(max - min - 1) + min;
+        return RandomStringUtils.random(len, 0x4e00, 0x9fa5, false, false);
+    }
+
+    /**
+     * Randomly generate English string.
+     *
+     * @param min min
+     * @param max max
+     * @return english string
+     */
+    public static String en(final int min, final int max) {
+        return RandomStringUtils.random(RandomUtil.randomInt(min, max), 5, 
129, true, false);
+    }
+
+    /**
+     * Randomly generate item of data.
+     *
+     * @param data data
+     * @return item
+     */
+    public static Object oneOf(final Object... data) {
+        return data[RandomUtil.randomInt(0, data.length - 1)];
+    }
+
+    /**
+     * Generate current time.
+     *
+     * @param formats formats
+     * @return time
+     */
+    public static String current(final String... formats) {
+        String format = DEFAULT_FORMAT;
+        if (Objects.nonNull(formats) && formats.length != 0 && 
Objects.nonNull(formats[0])) {
+            format = formats[0];
+        }
+        LocalDateTime now = LocalDateTime.now();
+        try {
+            return DateTimeFormatter.ofPattern(format).format(now);
+        } catch (DateTimeException e) {
+            LOG.warn("format fail,use default format :{}", DEFAULT_FORMAT);
+            return DateTimeFormatter.ofPattern(DEFAULT_FORMAT).format(now);
+        }
+
+    }
+
+    public static class FormatDouble {
+
+        private final Double val;
+
+        private final String format;
+
+        public FormatDouble(final Double val, final String format) {
+            this.val = val;
+            this.format = format;
+        }
+
+        public FormatDouble(final Double val) {
+            this.val = val;
+            this.format = null;
+        }
+
+        @Override
+        public String toString() {
+            if (Objects.isNull(format)) {
+                return val.toString();
+            }
+            return String.format(format, val);
+        }
+    }
+
+}
diff --git 
a/shenyu-plugin/shenyu-plugin-mock/src/main/resources/META-INF/shenyu/org.apache.shenyu.plugin.mock.generator.Generator
 
b/shenyu-plugin/shenyu-plugin-mock/src/main/resources/META-INF/shenyu/org.apache.shenyu.plugin.mock.generator.Generator
index fb1b6fd52..0e3f8b9ec 100644
--- 
a/shenyu-plugin/shenyu-plugin-mock/src/main/resources/META-INF/shenyu/org.apache.shenyu.plugin.mock.generator.Generator
+++ 
b/shenyu-plugin/shenyu-plugin-mock/src/main/resources/META-INF/shenyu/org.apache.shenyu.plugin.mock.generator.Generator
@@ -24,4 +24,5 @@ int=org.apache.shenyu.plugin.mock.generator.RandomIntGenerator
 list=org.apache.shenyu.plugin.mock.generator.RangeDataGenerator
 zh=org.apache.shenyu.plugin.mock.generator.ZhStringGenerator
 array=org.apache.shenyu.plugin.mock.generator.ArrayGenerator
+expression=org.apache.shenyu.plugin.mock.generator.ExpressionGenerator
 
diff --git 
a/shenyu-plugin/shenyu-plugin-mock/src/test/java/org/apache/shenyu/plugin/mock/generator/ArrayGeneratorTest.java
 
b/shenyu-plugin/shenyu-plugin-mock/src/test/java/org/apache/shenyu/plugin/mock/generator/ArrayGeneratorTest.java
index 852cf4a01..451510ccb 100644
--- 
a/shenyu-plugin/shenyu-plugin-mock/src/test/java/org/apache/shenyu/plugin/mock/generator/ArrayGeneratorTest.java
+++ 
b/shenyu-plugin/shenyu-plugin-mock/src/test/java/org/apache/shenyu/plugin/mock/generator/ArrayGeneratorTest.java
@@ -19,9 +19,9 @@ package org.apache.shenyu.plugin.mock.generator;
 
 import org.junit.jupiter.api.Test;
 
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 /**
  * The test case for {@link ArrayGenerator}.
@@ -42,6 +42,10 @@ public final class ArrayGeneratorTest {
         arrayGenerator.parseRule("array|{\"ints\":${array|10|3}}|3");
         String generate = arrayGenerator.generate();
         
assertEquals("{\"ints\":[10,10,10]},{\"ints\":[10,10,10]},{\"ints\":[10,10,10]}",
 generate);
+
+        arrayGenerator.parseRule("array|{\"int\":${expression|#oneOf(10)}}|3");
+        generate = arrayGenerator.generate();
+        assertEquals("{\"int\":10},{\"int\":10},{\"int\":10}", generate);
     }
 
     @Test
diff --git 
a/shenyu-plugin/shenyu-plugin-mock/src/test/java/org/apache/shenyu/plugin/mock/generator/ExpressionGeneratorTest.java
 
b/shenyu-plugin/shenyu-plugin-mock/src/test/java/org/apache/shenyu/plugin/mock/generator/ExpressionGeneratorTest.java
new file mode 100644
index 000000000..1a0df96c8
--- /dev/null
+++ 
b/shenyu-plugin/shenyu-plugin-mock/src/test/java/org/apache/shenyu/plugin/mock/generator/ExpressionGeneratorTest.java
@@ -0,0 +1,137 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.plugin.mock.generator;
+
+import org.apache.shenyu.common.utils.JsonUtils;
+import org.junit.jupiter.api.Test;
+
+import java.time.LocalDate;
+import java.util.Arrays;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.allOf;
+import static org.hamcrest.Matchers.greaterThanOrEqualTo;
+import static org.hamcrest.Matchers.in;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.lessThanOrEqualTo;
+import static org.hamcrest.Matchers.matchesRegex;
+import static org.hamcrest.Matchers.oneOf;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class ExpressionGeneratorTest {
+
+    private final ExpressionGenerator generator = new ExpressionGenerator();
+
+    @Test
+    public void testGenerate() {
+
+        generator.parseRule("expression|T(java.time.LocalDate).now()");
+        assertThat(generator.generate(), 
is(JsonUtils.toJson(LocalDate.now().toString())));
+
+        generator.parseRule("expression|1==1");
+        assertThat(generator.generate(), is("true"));
+    }
+
+    @Test
+    public void testBoolGenerate() {
+        generator.parseRule("expression|#bool()");
+        String generate = generator.generate();
+        assertThat(generate, in(Arrays.asList("true", "false")));
+
+    }
+
+    @Test
+    public void testCurrentTimeGenerate() {
+        generator.parseRule("expression|#current()");
+        assertTrue(generator.generate().matches("^\"\\d{4}(-\\d{2}){2} 
\\d{2}(:\\d{2}){2}\"$"));
+        generator.parseRule("expression|#current('YYYY-MM-dd')");
+        assertTrue(generator.generate().matches("^\"\\d{4}(-\\d{2}){2}\"$"));
+    }
+
+    @Test
+    public void testEmailTimeGenerate() {
+        generator.parseRule("expression|#email()");
+        assertNotNull(generator.generate());
+    }
+
+    @Test
+    public void testEnStringGenerate() {
+        int max = 10;
+        int min = 5;
+        generator.parseRule(String.format("expression|#en(%d,%d)", min, max));
+        String enString = generator.generate();
+        assertThat(enString, matchesRegex("\"[a-zA-Z]{" + min + "," + max + 
"}\""));
+    }
+
+    @Test
+    public void testPhoneGenerate() {
+        generator.parseRule("expression|#phone()");
+        String phone = generator.generate();
+        assertTrue(phone.matches("^\"1[3-9]\\d{9}\"$"));
+    }
+
+    @Test
+    public void testRandomDoubleGenerate() {
+        double min = 10.5;
+        double max = 12.0;
+        generator.parseRule(String.format("expression|#double(%f,%f)", min, 
max));
+        String doubleValue = generator.generate();
+        assertThat(Double.valueOf(doubleValue), 
allOf(greaterThanOrEqualTo(min), lessThanOrEqualTo(max)));
+
+        generator.parseRule("expression|#double(10.5,12.0,'¥%.2f')");
+        doubleValue = generator.generate();
+        assertThat(Double.valueOf(doubleValue.substring(1)), 
allOf(greaterThanOrEqualTo(min), lessThanOrEqualTo(max)));
+        assertThat(doubleValue, matchesRegex("^¥\\d+.\\d{2}$"));
+    }
+
+    @Test
+    public void testRandomIntGenerate() {
+        int min = 10;
+        int max = 15;
+        generator.parseRule(String.format("expression|#int(%d,%d)", min, max));
+        String val = generator.generate();
+        assertThat(Integer.valueOf(val), allOf(greaterThanOrEqualTo(min), 
lessThanOrEqualTo(max)));
+    }
+
+    @Test
+    public void testRandomDataGenerate() {
+
+        generator.parseRule("expression|#oneOf('shenyu','number',1)");
+        String val = generator.generate();
+        assertThat(val, oneOf("\"shenyu\"", "\"number\"", "1"));
+    }
+
+    @Test
+    public void testZhDataGenerate() {
+
+        int minLength = 10;
+        int maxLength = 20;
+        generator.parseRule(String.format("expression|#zh(%d,%d)", minLength, 
maxLength));
+        String val = generator.generate();
+        assertThat(val.length(), allOf(greaterThanOrEqualTo(minLength), 
lessThanOrEqualTo(maxLength)));
+    }
+
+    @Test
+    public void testMatch() {
+        assertTrue(generator.match("expression|23"));
+        assertFalse(generator.match("expression"));
+        assertFalse(generator.match("expression|"));
+    }
+}
diff --git 
a/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/generator/BoolGenerator.java
 
b/shenyu-plugin/shenyu-plugin-mock/src/test/java/org/apache/shenyu/plugin/mock/generator/GeneratorFactoryTest.java
similarity index 51%
copy from 
shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/generator/BoolGenerator.java
copy to 
shenyu-plugin/shenyu-plugin-mock/src/test/java/org/apache/shenyu/plugin/mock/generator/GeneratorFactoryTest.java
index 1233bccdb..e0ccd3854 100644
--- 
a/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/generator/BoolGenerator.java
+++ 
b/shenyu-plugin/shenyu-plugin-mock/src/test/java/org/apache/shenyu/plugin/mock/generator/GeneratorFactoryTest.java
@@ -17,32 +17,26 @@
 
 package org.apache.shenyu.plugin.mock.generator;
 
-import org.apache.shenyu.plugin.mock.util.RandomUtil;
-import org.apache.shenyu.spi.Join;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
 
-/**
- * Boolean Generator.
- */
-@Join
-public class BoolGenerator implements Generator<Boolean> {
-    
-    @Override
-    public String getName() {
-        return "bool";
-    }
-    
-    @Override
-    public Boolean generate() {
-        return RandomUtil.randomInt(0, 1) == 1;
-    }
-    
-    @Override
-    public int getParamSize() {
-        return 0;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.matchesRegex;
+
+public class GeneratorFactoryTest {
+
+    @Test
+    public void testDealRule() {
+        String dealedContent = GeneratorFactory.dealRule("${phone}");
+        assertThat(dealedContent, matchesRegex("^\"1[3-9]\\d{9}\"$"));
     }
-    
-    @Override
-    public boolean match(final String rule) {
-        return rule.matches("^bool$");
+
+    @ParameterizedTest
+    @ValueSource(strings = {"${expression|(sdxc}", "${wrong_rule|123}"})
+    public void testDealRuleWithWrongContent(final String content) {
+        String dealedContent = GeneratorFactory.dealRule(content);
+        assertThat(dealedContent, is("\"[#ERROR EXPRESSION#]\""));
     }
 }


Reply via email to