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

haiqi 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 d3927d43ee [type: feature] add e2e  grpc test (#4966)
d3927d43ee is described below

commit d3927d43ee70d3a784568f19b42cc889fbe8cf6c
Author: Misaya295 <[email protected]>
AuthorDate: Thu Aug 17 08:38:17 2023 +0800

    [type: feature] add e2e  grpc test (#4966)
---
 .github/workflows/e2e.yaml                         |   2 +-
 shenyu-e2e/shenyu-e2e-case/pom.xml                 |   1 +
 .../shenyu-e2e-case/shenyu-e2e-case-grpc/pom.xml   |  31 ++
 .../shenyu/e2e/testcase/grpc/DataSynHttpTest.java  |  74 ++++
 .../shenyu/e2e/testcase/grpc/DataSynNacosTest.java |  75 ++++
 .../e2e/testcase/grpc/DataSynZookeeperTest.java    |  75 ++++
 .../shenyu/e2e/testcase/grpc/GrpcPluginCases.java  | 449 +++++++++++++++++++++
 .../shenyu/e2e/testcase/grpc/GrpcPluginTest.java   | 156 +++++++
 .../src/test/resources/admin-application.yml       | 119 ++++++
 .../src/test/resources/bootstrap-application.yml   | 332 +++++++++++++++
 .../src/test/resources/docker-compose.mysql.yml    | 126 ++++++
 .../shenyu/e2e/client/admin/AdminClient.java       |  30 +-
 .../apache/shenyu/e2e/model/data/Condition.java    | 307 +++++++++++++-
 .../e2e/model/handle/GrpcSelectorHandle.java       | 219 ++++++++++
 .../shenyu/e2e/model/response/SelectorDTO.java     |  36 +-
 .../e2e/engine/scenario/function/HttpCheckers.java |  27 ++
 .../scenario/specification/ShenYuCaseSpec.java     |  16 +-
 .../src/main/resources/application.yml             |   2 +-
 18 files changed, 2036 insertions(+), 41 deletions(-)

diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml
index 6c58495c09..83bb1ce5bd 100644
--- a/.github/workflows/e2e.yaml
+++ b/.github/workflows/e2e.yaml
@@ -150,7 +150,7 @@ jobs:
     if: ${{ needs.changes.outputs.e2e == 'true' }}
     strategy:
       matrix:
-        case: [ "shenyu-e2e-case-spring-cloud", 
"shenyu-e2e-case-apache-dubbo", "shenyu-e2e-case-sofa", "shenyu-e2e-case-motan" 
]
+        case: [ "shenyu-e2e-case-spring-cloud", 
"shenyu-e2e-case-apache-dubbo", "shenyu-e2e-case-sofa", 
"shenyu-e2e-case-motan", "shenyu-e2e-case-grpc"]
     steps:
       - uses: actions/checkout@v3
         with:
diff --git a/shenyu-e2e/shenyu-e2e-case/pom.xml 
b/shenyu-e2e/shenyu-e2e-case/pom.xml
index 8bfeb47c1d..05e4a078f8 100644
--- a/shenyu-e2e/shenyu-e2e-case/pom.xml
+++ b/shenyu-e2e/shenyu-e2e-case/pom.xml
@@ -35,6 +35,7 @@
         <module>shenyu-e2e-case-apache-dubbo</module>
         <module>shenyu-e2e-case-sofa</module>
         <module>shenyu-e2e-case-motan</module>
+        <module>shenyu-e2e-case-grpc</module>
     </modules>
 
     <dependencies>
diff --git a/shenyu-e2e/shenyu-e2e-case/shenyu-e2e-case-grpc/pom.xml 
b/shenyu-e2e/shenyu-e2e-case/shenyu-e2e-case-grpc/pom.xml
new file mode 100644
index 0000000000..6e353a3433
--- /dev/null
+++ b/shenyu-e2e/shenyu-e2e-case/shenyu-e2e-case-grpc/pom.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0";
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+
+    <parent>
+        <groupId>org.apache.shenyu</groupId>
+        <artifactId>shenyu-e2e-case</artifactId>
+        <version>0.0.1-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>shenyu-e2e-case-grpc</artifactId>
+
+</project>
\ No newline at end of file
diff --git 
a/shenyu-e2e/shenyu-e2e-case/shenyu-e2e-case-grpc/src/test/java/org/apache/shenyu/e2e/testcase/grpc/DataSynHttpTest.java
 
b/shenyu-e2e/shenyu-e2e-case/shenyu-e2e-case-grpc/src/test/java/org/apache/shenyu/e2e/testcase/grpc/DataSynHttpTest.java
new file mode 100644
index 0000000000..1ce098ffbe
--- /dev/null
+++ 
b/shenyu-e2e/shenyu-e2e-case/shenyu-e2e-case-grpc/src/test/java/org/apache/shenyu/e2e/testcase/grpc/DataSynHttpTest.java
@@ -0,0 +1,74 @@
+/*
+ * 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.e2e.testcase.grpc;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import org.apache.shenyu.e2e.client.admin.AdminClient;
+import org.apache.shenyu.e2e.client.gateway.GatewayClient;
+import org.apache.shenyu.e2e.engine.annotation.ShenYuTest;
+import org.apache.shenyu.e2e.engine.config.ShenYuEngineConfigure;
+import org.apache.shenyu.e2e.model.data.MetaData;
+import org.apache.shenyu.e2e.model.data.RuleCacheData;
+import org.apache.shenyu.e2e.model.data.SelectorCacheData;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+
+/**
+ * Testing the correctness of Http data synchronization method.
+ */
+@ShenYuTest(
+        mode = ShenYuEngineConfigure.Mode.DOCKER,
+        services = {
+                @ShenYuTest.ServiceConfigure(
+                        serviceName = "admin",
+                        port = 9095,
+                        baseUrl = "http://{hostname:localhost}:9095";,
+                        parameters = {
+                                @ShenYuTest.Parameter(key = "username", value 
= "admin"),
+                                @ShenYuTest.Parameter(key = "password", value 
= "123456"),
+                                @ShenYuTest.Parameter(key = "dataSyn", value = 
"admin_http")
+                        }
+                ),
+                @ShenYuTest.ServiceConfigure(
+                        serviceName = "gateway",
+                        port = 9195,
+                        baseUrl = "http://{hostname:localhost}:9195";,
+                        type = 
ShenYuEngineConfigure.ServiceType.SHENYU_GATEWAY,
+                        parameters = {
+                                @ShenYuTest.Parameter(key = "dataSyn", value = 
"gateway_http")
+                        }
+                )
+        },
+        dockerComposeFile = "classpath:./docker-compose.mysql.yml"
+)
+public class DataSynHttpTest {
+
+    @Test
+    void testDataSyn(final AdminClient adminClient, final GatewayClient 
gatewayClient) throws InterruptedException, JsonProcessingException {
+        adminClient.login();
+        Thread.sleep(10000);
+        List<MetaData> metaDataCacheList = gatewayClient.getMetaDataCache();
+        List<SelectorCacheData> selectorCacheList = 
gatewayClient.getSelectorCache();
+        List<RuleCacheData> ruleCacheList = gatewayClient.getRuleCache();
+        Assertions.assertEquals(1, selectorCacheList.size());
+        Assertions.assertEquals(9, metaDataCacheList.size());
+        Assertions.assertEquals(9, ruleCacheList.size());
+    }
+}
diff --git 
a/shenyu-e2e/shenyu-e2e-case/shenyu-e2e-case-grpc/src/test/java/org/apache/shenyu/e2e/testcase/grpc/DataSynNacosTest.java
 
b/shenyu-e2e/shenyu-e2e-case/shenyu-e2e-case-grpc/src/test/java/org/apache/shenyu/e2e/testcase/grpc/DataSynNacosTest.java
new file mode 100644
index 0000000000..887875914a
--- /dev/null
+++ 
b/shenyu-e2e/shenyu-e2e-case/shenyu-e2e-case-grpc/src/test/java/org/apache/shenyu/e2e/testcase/grpc/DataSynNacosTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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.e2e.testcase.grpc;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import org.apache.shenyu.e2e.client.admin.AdminClient;
+import org.apache.shenyu.e2e.client.gateway.GatewayClient;
+import org.apache.shenyu.e2e.engine.annotation.ShenYuTest;
+import org.apache.shenyu.e2e.engine.annotation.ShenYuTest.Parameter;
+import org.apache.shenyu.e2e.engine.config.ShenYuEngineConfigure;
+import org.apache.shenyu.e2e.model.data.MetaData;
+import org.apache.shenyu.e2e.model.data.RuleCacheData;
+import org.apache.shenyu.e2e.model.data.SelectorCacheData;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+
+/**
+ * Testing the correctness of Nacos data synchronization method.
+ */
+@ShenYuTest(
+        mode = ShenYuEngineConfigure.Mode.DOCKER,
+        services = {
+                @ShenYuTest.ServiceConfigure(
+                        serviceName = "admin",
+                        port = 9095,
+                        baseUrl = "http://{hostname:localhost}:9095";,
+                        parameters = {
+                                @Parameter(key = "username", value = "admin"),
+                                @Parameter(key = "password", value = "123456"),
+                                @Parameter(key = "dataSyn", value = "nacos")
+                        }
+                ),
+                @ShenYuTest.ServiceConfigure(
+                        serviceName = "gateway",
+                        port = 9195,
+                        baseUrl = "http://{hostname:localhost}:9195";,
+                        type = 
ShenYuEngineConfigure.ServiceType.SHENYU_GATEWAY,
+                        parameters = {
+                                @Parameter(key = "dataSyn", value = "nacos")
+                        }
+                )
+        },
+        dockerComposeFile = "classpath:./docker-compose.mysql.yml"
+)
+public class DataSynNacosTest {
+
+    @Test
+    void testDataSyn(final AdminClient adminClient, final GatewayClient 
gatewayClient) throws InterruptedException, JsonProcessingException {
+        adminClient.login();
+        Thread.sleep(10000);
+        List<MetaData> metaDataCacheList = gatewayClient.getMetaDataCache();
+        List<SelectorCacheData> selectorCacheList = 
gatewayClient.getSelectorCache();
+        List<RuleCacheData> ruleCacheList = gatewayClient.getRuleCache();
+        Assertions.assertEquals(1, selectorCacheList.size());
+        Assertions.assertEquals(9, metaDataCacheList.size());
+        Assertions.assertEquals(9, ruleCacheList.size());
+    }
+}
diff --git 
a/shenyu-e2e/shenyu-e2e-case/shenyu-e2e-case-grpc/src/test/java/org/apache/shenyu/e2e/testcase/grpc/DataSynZookeeperTest.java
 
b/shenyu-e2e/shenyu-e2e-case/shenyu-e2e-case-grpc/src/test/java/org/apache/shenyu/e2e/testcase/grpc/DataSynZookeeperTest.java
new file mode 100644
index 0000000000..5b3a7433c4
--- /dev/null
+++ 
b/shenyu-e2e/shenyu-e2e-case/shenyu-e2e-case-grpc/src/test/java/org/apache/shenyu/e2e/testcase/grpc/DataSynZookeeperTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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.e2e.testcase.grpc;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import org.apache.shenyu.e2e.client.admin.AdminClient;
+import org.apache.shenyu.e2e.client.gateway.GatewayClient;
+import org.apache.shenyu.e2e.engine.annotation.ShenYuTest;
+import org.apache.shenyu.e2e.engine.config.ShenYuEngineConfigure;
+import org.apache.shenyu.e2e.model.data.MetaData;
+import org.apache.shenyu.e2e.model.data.RuleCacheData;
+import org.apache.shenyu.e2e.model.data.SelectorCacheData;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+
+/**
+ * Testing the correctness of Apollo data synchronization method.
+ */
+@ShenYuTest(
+        mode = ShenYuEngineConfigure.Mode.DOCKER,
+        services = {
+                @ShenYuTest.ServiceConfigure(
+                        serviceName = "admin",
+                        port = 9095,
+                        baseUrl = "http://{hostname:localhost}:9095";,
+                        parameters = {
+                                @ShenYuTest.Parameter(key = "username", value 
= "admin"),
+                                @ShenYuTest.Parameter(key = "password", value 
= "123456"),
+                                @ShenYuTest.Parameter(key = "dataSyn", value = 
"zookeeper")
+                        }
+                ),
+                @ShenYuTest.ServiceConfigure(
+                        serviceName = "gateway",
+                        port = 9195,
+                        baseUrl = "http://{hostname:localhost}:9195";,
+                        type = 
ShenYuEngineConfigure.ServiceType.SHENYU_GATEWAY,
+                        parameters = {
+                                @ShenYuTest.Parameter(key = "dataSyn", value = 
"zookeeper")
+                        }
+                )
+        },
+        dockerComposeFile = "classpath:./docker-compose.mysql.yml"
+)
+public class DataSynZookeeperTest {
+
+    @Test
+    void testDataSyn(final AdminClient adminClient, final GatewayClient 
gatewayClient) throws InterruptedException, JsonProcessingException {
+        adminClient.login();
+        Thread.sleep(10000);
+        List<MetaData> metaDataCacheList = gatewayClient.getMetaDataCache();
+        List<SelectorCacheData> selectorCacheList = 
gatewayClient.getSelectorCache();
+        List<RuleCacheData> ruleCacheList = gatewayClient.getRuleCache();
+        Assertions.assertEquals(1, selectorCacheList.size());
+        Assertions.assertEquals(9, metaDataCacheList.size());
+        Assertions.assertEquals(9, ruleCacheList.size());
+    }
+
+}
diff --git 
a/shenyu-e2e/shenyu-e2e-case/shenyu-e2e-case-grpc/src/test/java/org/apache/shenyu/e2e/testcase/grpc/GrpcPluginCases.java
 
b/shenyu-e2e/shenyu-e2e-case/shenyu-e2e-case-grpc/src/test/java/org/apache/shenyu/e2e/testcase/grpc/GrpcPluginCases.java
new file mode 100644
index 0000000000..de2182ac61
--- /dev/null
+++ 
b/shenyu-e2e/shenyu-e2e-case/shenyu-e2e-case-grpc/src/test/java/org/apache/shenyu/e2e/testcase/grpc/GrpcPluginCases.java
@@ -0,0 +1,449 @@
+/*
+ * 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.e2e.testcase.grpc;
+
+import com.google.common.collect.Lists;
+import io.restassured.http.Method;
+import org.apache.shenyu.e2e.engine.scenario.ShenYuScenarioProvider;
+import org.apache.shenyu.e2e.engine.scenario.specification.ScenarioSpec;
+import org.apache.shenyu.e2e.engine.scenario.specification.ShenYuAfterEachSpec;
+import 
org.apache.shenyu.e2e.engine.scenario.specification.ShenYuBeforeEachSpec;
+import org.apache.shenyu.e2e.engine.scenario.specification.ShenYuCaseSpec;
+import org.apache.shenyu.e2e.engine.scenario.specification.ShenYuScenarioSpec;
+import org.apache.shenyu.e2e.model.Plugin;
+import org.apache.shenyu.e2e.model.data.Condition;
+import org.apache.shenyu.e2e.model.handle.GrpcSelectorHandle;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import static 
org.apache.shenyu.e2e.engine.scenario.function.HttpCheckers.exists;
+import static 
org.apache.shenyu.e2e.engine.scenario.function.HttpCheckers.notExists;
+import static org.apache.shenyu.e2e.template.ResourceDataTemplate.newCondition;
+import static 
org.apache.shenyu.e2e.template.ResourceDataTemplate.newConditions;
+import static 
org.apache.shenyu.e2e.template.ResourceDataTemplate.newRuleBuilder;
+import static 
org.apache.shenyu.e2e.template.ResourceDataTemplate.newSelectorBuilder;
+
+public class GrpcPluginCases implements ShenYuScenarioProvider {
+
+    @Override
+    public List<ScenarioSpec> get() {
+        return Lists.newArrayList(
+                testWithUriEquals(),
+                testWithUriPathPattern(),
+                testWithUriStartWith(),
+                testWithEndWith(),
+                testWithMethodGet(),
+                testWithMethodPost(),
+                testWithMethodPut(),
+                testWithMethodDelete()
+        );
+    }
+
+    /**
+     * test with uri equal.
+     *
+     * @return ShenYuScenarioSpec
+     */
+    public ShenYuScenarioSpec testWithUriEquals() {
+        Map<String, List<MessageData>> body = new ConcurrentHashMap<>();
+        body.put("data", Lists.newArrayList(new MessageData("hello grpc")));
+        return ShenYuScenarioSpec.builder()
+                .name("single-grpc uri =]")
+                .beforeEachSpec(
+                        ShenYuBeforeEachSpec.builder()
+                                .addSelectorAndRule(
+                                        newSelectorBuilder("selector", 
Plugin.GRPC)
+                                                .name("/grpc")
+                                                
.conditionList(newConditions(Condition.ParamType.URI, Condition.Operator.EQUAL, 
"/grpc/echo"))
+                                                
.handle(GrpcSelectorHandle.builder()
+                                                        .status(true)
+                                                        
.upstreamUrl("grpc:38080")
+                                                        .weight(50).build())
+                                                .build(),
+                                        newRuleBuilder("rule")
+                                                
.conditionList(newConditions(Condition.ParamType.URI, Condition.Operator.EQUAL, 
"/grpc/echo"))
+                                                .build()
+                                )
+                                .checker(notExists("/sofa/findAll"))
+                                .waiting(exists(Method.POST, "/grpc/echo", 
body))
+                                .build()
+                )
+                .caseSpec(
+                        ShenYuCaseSpec.builder()
+                                .addExists(Method.POST, "/grpc/echo", body)
+                                .addNotExists("/grpc/fin")
+                                .addNotExists("/put")
+                                .addNotExists("/get")
+                                .build())
+                .afterEachSpec(ShenYuAfterEachSpec.DEFAULT)
+                .build();
+    }
+
+    /**
+     * test with uri path pattern.
+     *
+     * @return ShenYuScenarioSpec
+     */
+    public ShenYuScenarioSpec testWithUriPathPattern() {
+        Map<String, List<MessageData>> body = new ConcurrentHashMap<>();
+        body.put("data", Lists.newArrayList(new MessageData("hello grpc")));
+        return ShenYuScenarioSpec.builder()
+                .name("single-grpc uri path_pattern]")
+                .beforeEachSpec(
+                        ShenYuBeforeEachSpec.builder()
+                                .addSelectorAndRule(
+                                        newSelectorBuilder("selector", 
Plugin.GRPC)
+                                                .name("/grpc")
+                                                
.conditionList(newConditions(Condition.ParamType.URI, 
Condition.Operator.PATH_PATTERN, "/grpc/**"))
+                                                
.handle(GrpcSelectorHandle.builder()
+                                                        .status(true)
+                                                        
.upstreamUrl("grpc:38080")
+                                                        .weight(50).build())
+                                                .build(),
+                                        newRuleBuilder("rule")
+                                                
.conditionList(newConditions(Condition.ParamType.URI, 
Condition.Operator.PATH_PATTERN, "/grpc/**"))
+                                                .build()
+                                )
+                                .checker(notExists("/grpc/findAll"))
+                                .waiting(exists(Method.POST, "/grpc/echo", 
body))
+                                .build()
+                ).caseSpec(
+                        ShenYuCaseSpec.builder()
+                                .addExists(Method.POST, "/grpc/echo", body)
+                                .addExists(Method.POST, "/grpc/unaryFun", body)
+                                .addExists(Method.POST, 
"/grpc/bidiStreamingFun", body)
+                                .addExists(Method.POST, 
"/grpc/serverStreamingFun", body)
+                                .addNotExists(Method.GET, "/grp")
+                                .addNotExists(Method.POST, "/grpc/findAll")
+                                .addNotExists(Method.PUT, "/grpc/findAll")
+                                .addNotExists(Method.DELETE, "/grpc/findAll")
+                                .build())
+                .afterEachSpec(ShenYuAfterEachSpec.DEFAULT)
+                .build();
+    }
+
+    /**
+     * test with uri start with.
+     *
+     * @return ShenYuScenarioSpec
+     */
+    public ShenYuScenarioSpec testWithUriStartWith() {
+        Map<String, List<MessageData>> body = new ConcurrentHashMap<>();
+        body.put("data", Lists.newArrayList(new MessageData("hello grpc")));
+        return ShenYuScenarioSpec.builder()
+                .name("single-grpc uri path_pattern]")
+                .beforeEachSpec(
+                        ShenYuBeforeEachSpec.builder()
+                                .addSelectorAndRule(
+                                        newSelectorBuilder("selector", 
Plugin.GRPC)
+                                                .name("/grpc")
+                                                
.conditionList(newConditions(Condition.ParamType.URI, 
Condition.Operator.STARTS_WITH, "/grpc/"))
+                                                
.handle(GrpcSelectorHandle.builder()
+                                                        .status(true)
+                                                        
.upstreamUrl("grpc:38080")
+                                                        .weight(50).build())
+                                                .build(),
+                                        newRuleBuilder("rule")
+                                                
.conditionList(newConditions(Condition.ParamType.URI, 
Condition.Operator.STARTS_WITH, "/grpc/"))
+                                                .build()
+                                )
+                                .checker(notExists("/grpc/findAll"))
+                                .waiting(exists(Method.POST, "/grpc/echo", 
body))
+                                .build()
+                ).caseSpec(
+                        ShenYuCaseSpec.builder()
+                                .addExists(Method.POST, "/grpc/echo", body)
+                                .addExists(Method.POST, "/grpc/unaryFun", body)
+                                .addExists(Method.POST, 
"/grpc/bidiStreamingFun", body)
+                                .addExists(Method.POST, 
"/grpc/serverStreamingFun", body)
+                                .addNotExists(Method.POST, "/grpc/findAll")
+                                .addNotExists(Method.PUT, "/grpc/findAll")
+                                .addNotExists(Method.DELETE, "/grpc/findAll")
+                                .build())
+                .afterEachSpec(ShenYuAfterEachSpec.DEFAULT)
+                .build();
+    }
+
+    /**
+     * test with uri end with.
+     *
+     * @return ShenYuScenarioSpec
+     */
+    public ShenYuScenarioSpec testWithEndWith() {
+        Map<String, List<MessageData>> body = new ConcurrentHashMap<>();
+        body.put("data", Lists.newArrayList(new MessageData("hello grpc")));
+        return ShenYuScenarioSpec.builder()
+                .name("single-grpc uri path_pattern]")
+                .beforeEachSpec(
+                        ShenYuBeforeEachSpec.builder()
+                                .addSelectorAndRule(
+                                        newSelectorBuilder("selector", 
Plugin.GRPC)
+                                                .name("/grpc")
+                                                
.conditionList(newConditions(Condition.ParamType.URI, 
Condition.Operator.ENDS_WITH, "/echo"))
+                                                
.handle(GrpcSelectorHandle.builder()
+                                                        .status(true)
+                                                        
.upstreamUrl("grpc:38080")
+                                                        .weight(50).build())
+                                                .build(),
+                                        newRuleBuilder("rule")
+                                                
.conditionList(newConditions(Condition.ParamType.URI, 
Condition.Operator.ENDS_WITH, "/echo"))
+                                                .build()
+                                )
+                                .checker(notExists("/grpc/findAll"))
+                                .waiting(exists(Method.POST, "/grpc/echo", 
body))
+                                .build()
+                ).caseSpec(
+                        ShenYuCaseSpec.builder()
+                                .addExists(Method.POST, "/grpc/echo", body)
+                                .addNotExists("/grp")
+                                .addNotExists(Method.POST, "/grpc/findAll")
+                                .addNotExists(Method.PUT, "/grpc/findAll")
+                                .addNotExists(Method.DELETE, "/grpc/findAll")
+                                .build())
+                .afterEachSpec(ShenYuAfterEachSpec.DEFAULT)
+                .build();
+    }
+
+    /**
+     * test with uri method get.
+     *
+     * @return ShenYuScenarioSpec
+     */
+    public ShenYuScenarioSpec testWithMethodGet() {
+        Map<String, List<MessageData>> body = new ConcurrentHashMap<>();
+        body.put("data", Lists.newArrayList(new MessageData("hello grpc")));
+        return ShenYuScenarioSpec.builder()
+                .name("single-grpc uri method GET]")
+                .beforeEachSpec(
+                        ShenYuBeforeEachSpec.builder()
+                                .addSelectorAndRule(
+                                        newSelectorBuilder("selector", 
Plugin.GRPC)
+                                                .name("/grpc")
+                                                
.conditionList(Lists.newArrayList(
+                                                        
newCondition(Condition.ParamType.METHOD, Condition.Operator.EQUAL, "GET"),
+                                                        
newCondition(Condition.ParamType.URI, Condition.Operator.STARTS_WITH, "/grpc/")
+                                                ))
+                                                
.handle(GrpcSelectorHandle.builder()
+                                                        .status(true)
+                                                        
.upstreamUrl("grpc:38080")
+                                                        .weight(50).build())
+                                                .build(),
+                                        newRuleBuilder("rule")
+                                                
.conditionList(Lists.newArrayList(
+                                                        
newCondition(Condition.ParamType.METHOD, Condition.Operator.EQUAL, "GET"),
+                                                        
newCondition(Condition.ParamType.URI, Condition.Operator.EQUAL, "/grpc/echo")
+                                                ))
+                                                .build()
+                                )
+                                .checker(notExists(Method.GET, 
"/grpc/findAll"))
+                                .waiting(notExists(Method.GET, 
"/grpc/findAll"))
+                                .build()
+                )
+                .caseSpec(
+                        ShenYuCaseSpec.builder()
+                                .addExists(Method.POST, "/grpc/echo", body)
+                                .addNotExists(Method.GET, "/grpc/find")
+                                .addNotExists(Method.POST, "/grpc/findAll")
+                                .addNotExists(Method.PUT, "/grpc/findAll")
+                                .addNotExists(Method.DELETE, "/grpc/findAll")
+                                .build())
+                .afterEachSpec(ShenYuAfterEachSpec.DEFAULT)
+                .build();
+    }
+
+    /**
+     * test with uri method post.
+     *
+     * @return ShenYuScenarioSpec
+     */
+    public ShenYuScenarioSpec testWithMethodPost() {
+        Map<String, List<MessageData>> body = new ConcurrentHashMap<>();
+        body.put("data", Lists.newArrayList(new MessageData("hello grpc")));
+        return ShenYuScenarioSpec.builder()
+                .name("single-grpc uri method POST]")
+                .beforeEachSpec(
+                        ShenYuBeforeEachSpec.builder()
+                                .addSelectorAndRule(
+                                        newSelectorBuilder("selector", 
Plugin.GRPC)
+                                                .name("/grpc")
+                                                
.conditionList(Lists.newArrayList(
+                                                        
newCondition(Condition.ParamType.METHOD, Condition.Operator.EQUAL, "POST"),
+                                                        
newCondition(Condition.ParamType.URI, Condition.Operator.STARTS_WITH, "/grpc/")
+                                                ))
+                                                
.handle(GrpcSelectorHandle.builder()
+                                                        .status(true)
+                                                        
.upstreamUrl("grpc:38080")
+                                                        .weight(50).build())
+                                                .build(),
+                                        newRuleBuilder("rule")
+                                                
.conditionList(Lists.newArrayList(
+                                                        
newCondition(Condition.ParamType.METHOD, Condition.Operator.EQUAL, "POST"),
+                                                        
newCondition(Condition.ParamType.URI, Condition.Operator.EQUAL, "/grpc/echo")
+                                                ))
+                                                .build()
+                                )
+                                .checker(notExists(Method.POST, 
"/grpc/findAll"))
+                                .waiting(exists(Method.POST, "/grpc/echo", 
body))
+                                .build()
+                )
+                .caseSpec(
+                        ShenYuCaseSpec.builder()
+                                .addExists(Method.POST, "/grpc/echo", body)
+                                .addNotExists(Method.POST, "/grpc/find")
+                                .addNotExists(Method.GET, "/grpc/findAll")
+                                .addNotExists(Method.PUT, "/grpc/findAll")
+                                .addNotExists(Method.DELETE, "/grpc/findAll")
+                                .build())
+                .afterEachSpec(ShenYuAfterEachSpec.DEFAULT)
+                .build();
+    }
+
+    /**
+     * test with uri method put.
+     *
+     * @return ShenYuScenarioSpec
+     */
+    public ShenYuScenarioSpec testWithMethodPut() {
+        Map<String, List<MessageData>> body = new ConcurrentHashMap<>();
+        body.put("data", Lists.newArrayList(new MessageData("hello grpc")));
+        return ShenYuScenarioSpec.builder()
+                .name("single-grpc uri method PUT]")
+                .beforeEachSpec(
+                        ShenYuBeforeEachSpec.builder()
+                                .addSelectorAndRule(
+                                        newSelectorBuilder("selector", 
Plugin.GRPC)
+                                                .name("/grpc")
+                                                
.conditionList(Lists.newArrayList(
+                                                        
newCondition(Condition.ParamType.METHOD, Condition.Operator.EQUAL, "PUT"),
+                                                        
newCondition(Condition.ParamType.URI, Condition.Operator.STARTS_WITH, "/grpc/")
+                                                ))
+                                                
.handle(GrpcSelectorHandle.builder()
+                                                        .status(true)
+                                                        
.upstreamUrl("grpc:38080")
+                                                        .weight(50).build())
+                                                .build(),
+                                        newRuleBuilder("rule")
+                                                
.conditionList(Lists.newArrayList(
+                                                        
newCondition(Condition.ParamType.METHOD, Condition.Operator.EQUAL, "PUT"),
+                                                        
newCondition(Condition.ParamType.URI, Condition.Operator.EQUAL, "/grpc/echo")
+                                                ))
+                                                .build()
+                                )
+                                .checker(notExists(Method.PUT, 
"/grpc/findAll"))
+                                .waiting(exists(Method.POST, "/grpc/echo", 
body))
+                                .build()
+                )
+                .caseSpec(
+                        ShenYuCaseSpec.builder()
+                                .addExists(Method.POST, "/grpc/echo", body)
+                                .addNotExists(Method.PUT, "/grpc/find")
+                                .addNotExists(Method.GET, "/grpc/findAll")
+                                .addNotExists(Method.POST, "/grpc/findAll")
+                                .addNotExists(Method.DELETE, "/grpc/findAll")
+                                .build())
+                .afterEachSpec(ShenYuAfterEachSpec.DEFAULT)
+                .build();
+    }
+
+    /**
+     * test with uri method delete.
+     *
+     * @return ShenYuScenarioSpec
+     */
+    public ShenYuScenarioSpec testWithMethodDelete() {
+        Map<String, List<MessageData>> body = new ConcurrentHashMap<>();
+        body.put("data", Lists.newArrayList(new MessageData("hello grpc")));
+        return ShenYuScenarioSpec.builder()
+                .name("single-grpc uri method DELETE]")
+                .beforeEachSpec(
+                        ShenYuBeforeEachSpec.builder()
+                                .addSelectorAndRule(
+                                        newSelectorBuilder("selector", 
Plugin.GRPC)
+                                                .name("/grpc")
+                                                
.conditionList(Lists.newArrayList(
+                                                        
newCondition(Condition.ParamType.METHOD, Condition.Operator.EQUAL, "DELETE"),
+                                                        
newCondition(Condition.ParamType.URI, Condition.Operator.STARTS_WITH, 
"/grpc/echo")
+                                                ))
+                                                
.handle(GrpcSelectorHandle.builder()
+                                                        .status(true)
+                                                        
.upstreamUrl("grpc:38080")
+                                                        .weight(50).build())
+                                                .build(),
+                                        newRuleBuilder("rule")
+                                                
.conditionList(Lists.newArrayList(
+                                                        
newCondition(Condition.ParamType.METHOD, Condition.Operator.EQUAL, "DELETE"),
+                                                        
newCondition(Condition.ParamType.URI, Condition.Operator.EQUAL, "/grpc/echo")
+                                                ))
+                                                .build()
+                                )
+                                .checker(notExists(Method.DELETE, 
"/grpc/findAll"))
+                                .waiting(exists(Method.POST, "/grpc/echo", 
body))
+                                .build()
+                )
+                .caseSpec(
+                        ShenYuCaseSpec.builder()
+                                .addExists(Method.POST, "/grpc/echo", body)
+                                .addNotExists(Method.DELETE, "/grpc/find")
+                                .addNotExists(Method.GET, "/grpc/findAll")
+                                .addNotExists(Method.POST, "/grpc/findAll")
+                                .addNotExists(Method.PUT, "/grpc/findAll")
+                                .build())
+                .afterEachSpec(ShenYuAfterEachSpec.DEFAULT)
+                .build();
+    }
+
+    public static class MessageData {
+
+        /**
+         * message.
+         */
+        private String message;
+
+        /**
+         * default constructor.
+         */
+        public MessageData() {
+        }
+
+        public MessageData(final String message) {
+            this.message = message;
+        }
+
+        /**
+         * get message.
+         *
+         * @return message
+         */
+        public String getMessage() {
+            return message;
+        }
+
+        /**
+         * set message.
+         *
+         * @param message message
+         */
+        public void setMessage(final String message) {
+            this.message = message;
+        }
+
+    }
+
+}
diff --git 
a/shenyu-e2e/shenyu-e2e-case/shenyu-e2e-case-grpc/src/test/java/org/apache/shenyu/e2e/testcase/grpc/GrpcPluginTest.java
 
b/shenyu-e2e/shenyu-e2e-case/shenyu-e2e-case-grpc/src/test/java/org/apache/shenyu/e2e/testcase/grpc/GrpcPluginTest.java
new file mode 100644
index 0000000000..fd04270c3b
--- /dev/null
+++ 
b/shenyu-e2e/shenyu-e2e-case/shenyu-e2e-case-grpc/src/test/java/org/apache/shenyu/e2e/testcase/grpc/GrpcPluginTest.java
@@ -0,0 +1,156 @@
+/*
+ * 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.e2e.testcase.grpc;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import org.apache.shenyu.e2e.client.admin.AdminClient;
+import org.apache.shenyu.e2e.client.gateway.GatewayClient;
+import org.apache.shenyu.e2e.engine.annotation.ShenYuScenario;
+import org.apache.shenyu.e2e.engine.annotation.ShenYuTest;
+import org.apache.shenyu.e2e.engine.config.ShenYuEngineConfigure;
+import org.apache.shenyu.e2e.engine.scenario.specification.BeforeEachSpec;
+import org.apache.shenyu.e2e.engine.scenario.specification.CaseSpec;
+import org.apache.shenyu.e2e.model.ResourcesData;
+import org.apache.shenyu.e2e.model.data.MetaData;
+import org.apache.shenyu.e2e.model.data.RuleCacheData;
+import org.apache.shenyu.e2e.model.data.SelectorCacheData;
+import org.apache.shenyu.e2e.model.data.SelectorData;
+import org.apache.shenyu.e2e.model.response.MetaDataDTO;
+import org.apache.shenyu.e2e.model.response.RuleDTO;
+import org.apache.shenyu.e2e.model.response.SelectorDTO;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import org.testcontainers.shaded.com.google.common.collect.Lists;
+
+import java.util.List;
+
+@ShenYuTest(
+        mode = ShenYuEngineConfigure.Mode.DOCKER,
+        services = {
+                @ShenYuTest.ServiceConfigure(
+                        serviceName = "admin",
+                        port = 9095,
+                        baseUrl = "http://{hostname:localhost}:9095";,
+                        parameters = {
+                                @ShenYuTest.Parameter(key = "username", value 
= "admin"),
+                                @ShenYuTest.Parameter(key = "password", value 
= "123456"),
+                                @ShenYuTest.Parameter(key = "dataSyn", value = 
"admin_websocket")
+                        }
+                ),
+                @ShenYuTest.ServiceConfigure(
+                        serviceName = "gateway",
+                        port = 9195,
+                        baseUrl = "http://{hostname:localhost}:9195";,
+                        type = 
ShenYuEngineConfigure.ServiceType.SHENYU_GATEWAY,
+                        parameters = {
+                                @ShenYuTest.Parameter(key = "dataSyn", value = 
"gateway_websocket")
+                        }
+                )
+        },
+        dockerComposeFile = "classpath:./docker-compose.mysql.yml"
+)
+/*
+  Testing grpc plugin.
+ */
+public class GrpcPluginTest {
+
+    private static String selectorId = "";
+
+    private static String condictionId = "";
+
+    private List<String> selectorIds = Lists.newArrayList();
+
+    @BeforeAll
+    void setup(final AdminClient adminClient, final GatewayClient 
gatewayClient) throws InterruptedException, JsonProcessingException {
+        adminClient.login();
+        Thread.sleep(10000);
+        final List<SelectorDTO> selectorDTOList = 
adminClient.listAllSelectors();
+        final List<MetaDataDTO> metaDataDTOList = 
adminClient.listAllMetaData();
+        final List<RuleDTO> ruleDTOList = adminClient.listAllRules();
+        selectorId = selectorDTOList.get(0).getId();
+        selectorIds.add(selectorId);
+        SelectorDTO selector = adminClient.getSelector(selectorId);
+        condictionId = selector.getConditionList().get(0).getId();
+        Assertions.assertEquals(1, selectorDTOList.size());
+        Assertions.assertEquals(9, metaDataDTOList.size());
+        Assertions.assertEquals(9, ruleDTOList.size());
+
+        List<MetaData> metaDataCacheList = gatewayClient.getMetaDataCache();
+        List<SelectorCacheData> selectorCacheList = 
gatewayClient.getSelectorCache();
+        List<RuleCacheData> ruleCacheList = gatewayClient.getRuleCache();
+        Assertions.assertEquals(1, selectorCacheList.size());
+        Assertions.assertEquals(9, metaDataCacheList.size());
+        Assertions.assertEquals(9, ruleCacheList.size());
+
+        MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
+        formData.add("id", "15");
+        formData.add("name", "grpc");
+        formData.add("enabled", "true");
+        formData.add("role", "Proxy");
+        formData.add("sort", "310");
+        formData.add("config", 
"{\"multiSelectorHandle\":\"1\",\"multiRuleHandle\":\"0\",\"threadpool\":\"shared\"}");
+        adminClient.changePluginStatus("15", formData);
+        adminClient.deleteAllRules(selectorId);
+    }
+
+    @BeforeEach
+    void before(final AdminClient client, final GatewayClient gateway, final 
BeforeEachSpec spec) {
+        spec.getChecker().check(gateway);
+        ResourcesData resources = spec.getResources();
+        for (ResourcesData.Resource res : resources.getResources()) {
+            SelectorData selector = res.getSelector();
+            selector.setId(selectorId);
+            selector.getConditionList().forEach(
+                condition -> {
+                    condition.setSelectorId(selectorId);
+                    condition.setId(condictionId);
+                }
+            );
+            client.changeSelector(selector.getId(), selector);
+
+            res.getRules().forEach(rule -> {
+                rule.setSelectorId(selectorId);
+                client.create(rule);
+            });
+        }
+
+        spec.getWaiting().waitFor(gateway);
+    }
+
+    @ShenYuScenario(provider = GrpcPluginCases.class)
+    void testGrpc(final GatewayClient gateway, final CaseSpec spec) {
+        spec.getVerifiers().forEach(verifier -> 
verifier.verify(gateway.getHttpRequesterSupplier().get()));
+    }
+
+    @AfterAll
+    static void teardown(final AdminClient client) {
+        client.deleteAllSelectors();
+        MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
+        formData.add("id", "15");
+        formData.add("name", "grpc");
+        formData.add("enabled", "false");
+        formData.add("role", "Proxy");
+        formData.add("sort", "310");
+        client.changePluginStatus("15", formData);
+    }
+}
+
diff --git 
a/shenyu-e2e/shenyu-e2e-case/shenyu-e2e-case-grpc/src/test/resources/admin-application.yml
 
b/shenyu-e2e/shenyu-e2e-case/shenyu-e2e-case-grpc/src/test/resources/admin-application.yml
new file mode 100755
index 0000000000..504485f9f2
--- /dev/null
+++ 
b/shenyu-e2e/shenyu-e2e-case/shenyu-e2e-case-grpc/src/test/resources/admin-application.yml
@@ -0,0 +1,119 @@
+# 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.
+
+server:
+  port: 9095
+  address: 0.0.0.0
+
+spring:
+  profiles:
+    active: h2
+  thymeleaf:
+    cache: true
+    encoding: utf-8
+    enabled: true
+    prefix: classpath:/static/
+    suffix: .html
+  mvc:
+    pathmatch:
+      matching-strategy: ant_path_matcher
+  jackson:
+    time-zone: GMT+8 # GMT , Asia/Shanghai
+  messages:
+    basename: message/i18n
+
+management:
+  endpoints:
+    web:
+      exposure:
+        include:
+          - 'health'
+          - 'prometheus'
+    enabled-by-default: true
+
+mybatis:
+  config-location: classpath:/mybatis/mybatis-config.xml
+  mapper-locations: classpath:/mappers/*.xml
+
+shenyu:
+  register:
+    registerType: http #http #zookeeper #etcd #nacos #consul
+    serverLists: #localhost:2181 #http://localhost:2379 #localhost:8848
+    props:
+      sessionTimeout: 5000
+      connectionTimeout: 2000
+      checked: true
+      zombieCheckThreads: 10
+      zombieCheckTimes: 5
+      scheduledTime: 10
+      nacosNameSpace: ShenyuRegisterCenter
+  sync:
+    websocket:
+      enabled: true
+      messageMaxSize: 10240
+      allowOrigins: ws://localhost:9095;ws://localhost:9195;
+  ldap:
+    enabled: false
+    url: ldap://xxxx:xxx
+    bind-dn: cn=xxx,dc=xxx,dc=xxx
+    password: xxxx
+    base-dn: ou=xxx,dc=xxx,dc=xxx
+    object-class: person
+    login-field: cn
+  jwt:
+    expired-seconds: 86400000
+  shiro:
+    white-list:
+      - /
+      - /favicon.*
+      - /static/**
+      - /index**
+      - /platform/login
+      - /websocket
+      - /error
+      - /actuator/health
+      - /actuator/health/** # /actuator/health/readiness 
/actuator/health/liveness
+      - /actuator/prometheus
+      - /swagger-ui.html
+      - /webjars/**
+      - /swagger-resources/**
+      - /v2/api-docs
+      - /csrf
+  swagger:
+    enable: true
+  dashboard:
+    core:
+      onlySuperAdminPermission:
+        - system:manager:add
+        - system:manager:edit
+        - system:manager:delete
+        - system:role:add
+        - system:role:edit
+        - system:role:delete
+        - system:resource:addButton
+        - system:resource:addMenu
+        - system:resource:editButton
+        - system:resource:editMenu
+        - system:resource:deleteButton
+        - system:resource:deleteMenu
+
+logging:
+  level:
+    root: info
+    org.springframework.boot: info
+    org.apache.ibatis: info
+    org.apache.shenyu.bonuspoint: info
+    org.apache.shenyu.lottery: info
+    org.apache.shenyu: info
diff --git 
a/shenyu-e2e/shenyu-e2e-case/shenyu-e2e-case-grpc/src/test/resources/bootstrap-application.yml
 
b/shenyu-e2e/shenyu-e2e-case/shenyu-e2e-case-grpc/src/test/resources/bootstrap-application.yml
new file mode 100644
index 0000000000..c1d7ff61ec
--- /dev/null
+++ 
b/shenyu-e2e/shenyu-e2e-case/shenyu-e2e-case-grpc/src/test/resources/bootstrap-application.yml
@@ -0,0 +1,332 @@
+# 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.
+
+server:
+  port: 9195
+  address: 0.0.0.0
+  compression:
+    enabled: true
+    minResponseSize: 1048576 # If the response data is greater than 1M, enable 
compression.
+
+spring:
+  main:
+    allow-bean-definition-overriding: true
+  application:
+    name: shenyu-bootstrap
+  codec:
+    max-in-memory-size: 2MB
+  cloud:
+    discovery:
+      enabled: false
+    nacos:
+      discovery:
+        server-addr: 127.0.0.1:8848 # Spring Cloud Alibaba Dubbo use this.
+        enabled: false
+        namespace: ShenyuRegisterCenter
+
+# if you want use ribbon please config every server.
+#springCloud-test:
+#  ribbon:
+#    NIWSServerListClassName: 
com.netflix.niws.loadbalancer.DiscoveryEnabledNIWSServerList
+
+eureka:
+  client:
+    enabled: false
+    serviceUrl:
+      defaultZone: http://localhost:8761/eureka/
+  instance:
+    prefer-ip-address: true
+
+#  security:
+#    oauth2:
+#      client:
+#        registration:
+#          <your client-registration-id>:
+#            client-id: <your client-id>
+#            client-secret: <your client-secret>
+#        provider:
+#          <your client-registration-id>:
+#            authorization-uri: <your authorization-uri>
+#            token-uri: <your access-token-uri>
+#            user-info-uri: <your user-info-uri>
+#            jwk-set-uri: <your jwk-set-uri>
+
+management:
+  health:
+    redis:
+      enabled: false
+    elasticsearch:
+      enabled: false
+  endpoint:
+    health:
+      enabled: true
+      show-details: always
+  endpoints:
+    web:
+      exposure:
+        include: "*" # or health,info
+
+
+shenyu:
+  selectorMatchCache:
+    ## selector L1 cache
+    cache:
+      enabled: false
+      initialCapacity: 10000 # initial capacity in cache
+      maximumSize: 10000 # max size in cache
+    ## selector L2 cache, use trie as L2 cache
+    trie:
+      enabled: false
+      cacheSize: 128 # the number of plug-ins
+      matchMode: antPathMatch
+  ruleMatchCache:
+    ## rule L1 cache
+    cache:
+      enabled: true
+      initialCapacity: 10000 # initial capacity in cache
+      maximumSize: 65536 # max size in cache
+    ## rule L2 cache, use trie as L2 cache
+    trie:
+      enabled: false
+      cacheSize: 1024 # the number of selectors
+      matchMode: antPathMatch
+  netty:
+    http:
+      # set to false, user can custom the netty tcp server config.
+      webServerFactoryEnabled: true
+      selectCount: 1
+      workerCount: 8
+      accessLog: false
+      serverSocketChannel:
+        soRcvBuf: 87380
+        soBackLog: 128
+        soReuseAddr: false
+        connectTimeoutMillis: 10000
+        writeBufferHighWaterMark: 65536
+        writeBufferLowWaterMark: 32768
+        writeSpinCount: 16
+        autoRead: false
+        allocType: "pooled"
+        messageSizeEstimator: 8
+        singleEventExecutorPerGroup: true
+      socketChannel:
+        soKeepAlive: false
+        soReuseAddr: false
+        soLinger: -1
+        tcpNoDelay: true
+        soRcvBuf: 87380
+        soSndBuf: 16384
+        ipTos: 0
+        allowHalfClosure: false
+        connectTimeoutMillis: 10000
+        writeBufferHighWaterMark: 65536
+        writeBufferLowWaterMark: 32768
+        writeSpinCount: 16
+        autoRead: false
+        allocType: "pooled"
+        messageSizeEstimator: 8
+        singleEventExecutorPerGroup: true
+      sni:
+        enabled: false
+        mod: k8s #manul
+        defaultK8sSecretNamespace: shenyu-ingress
+        defaultK8sSecretName: default-cert
+  #        mod: manual
+  #        certificates:
+  #          - domain: 'localhost'
+  #            keyCertChainFile: 
'/Users/zhukunshuai/Desktop/cert/example.com+1.pem'
+  #            keyFile: '/Users/zhukunshuai/Desktop/cert/example.com+1-key.pem'
+  #          - domain: 'example.com'
+  #            keyCertChainFile: 
'/Users/zhukunshuai/Desktop/cert/example.com+1.pem'
+  #            keyFile: '/Users/zhukunshuai/Desktop/cert/example.com+1-key.pem'
+  #  httpclient:
+  #    strategy: webClient # netty
+  #    connectTimeout: 45000
+  #    responseTimeout: 3000
+  #    readerIdleTime: 3000
+  #    writerIdleTime: 3000
+  #    allIdleTime: 3000
+  #    readTimeout: 3000
+  #    writeTimeout: 3000
+  #    wiretap: false
+  #    keepAlive: false
+  #    maxInMemorySize: 1 #1mb
+  #    pool:
+  #      type: ELASTIC
+  #      name: proxy
+  #      maxConnections: 16
+  #      acquireTimeout: 45000
+  #      maxIdleTime: 3000
+  #    proxy:
+  #      host:
+  #      port:
+  #      username:
+  #      password:
+  #      nonProxyHostsPattern:
+  #    ssl:
+  #      useInsecureTrustManager: true
+  #      keyStoreType: PKCS12
+  #      keyStorePath: classpath:keystore.p12
+  #      keyStorePassword: 123456
+  #      keyStoreProvider:
+  #      keyPassword: 123456
+  #      trustedX509Certificates:
+  #      handshakeTimeout:
+  #      closeNotifyFlushTimeout:
+  #      closeNotifyReadTimeout:
+  #      defaultConfigurationType:
+  #    threadPool:
+  #      prefix: shenyu
+  #      selectCount: 1
+  #      workerCount: 8
+  #      daemon: true
+  register:
+    enabled: false
+    registerType: zookeeper #etcd #consul
+    serverLists: localhost:2181 #http://localhost:2379 #localhost:8848
+    props:
+  cross:
+    enabled: true
+    allowedHeaders:
+    allowedMethods: "*"
+    allowedAnyOrigin: true # the same of Access-Control-Allow-Origin: "*"
+      #    allowedOrigin:
+    # format : schema://prefix spacer domain
+    # Access-Control-Allow-Origin: "http://a.apache.org,http://b.apache.org";
+    #      spacer: "."
+    #      domain: apache.org
+    #      prefixes:
+    #        - a # a.apache.org
+    #        - b # b.apache.org
+    #      origins:
+    #        - c.apache.org
+    #        - d.apache.org
+    #        - http://e.apache.org
+    #      originRegex: ^http(|s)://(.*\.|)abc.com$
+    allowedExpose: ""
+    maxAge: "18000"
+    allowCredentials: true
+
+  switchConfig:
+    local: true
+    collapseSlashes: false
+  file:
+    enabled: true
+    maxSize : 10
+  sync:
+    websocket:
+      urls: ws://localhost:9095/websocket
+      allowOrigin: ws://localhost:9195
+  #    apollo:
+  #      appId: shenyu
+  #      meta: http://localhost:8080
+  #      env: dev
+  #      clusterName: test
+  #      namespace: application
+  #    zookeeper:
+  #      url: localhost:2181
+  #      sessionTimeout: 5000
+  #      connectionTimeout: 2000
+  #    http:
+  #      url: http://localhost:9095
+  #      username:
+  #      password:
+  #    nacos:
+  #      url: localhost:8848
+  #      namespace: 1c10d748-af86-43b9-8265-75f487d20c6c
+  #      username:
+  #      password:
+  #      acm:
+  #        enabled: false
+  #        endpoint: acm.aliyun.com
+  #        namespace:
+  #        accessKey:
+  #        secretKey:
+  #    etcd:
+  #      url: http://localhost:2379
+  #    consul:
+  #      url: http://localhost:8500
+  #      waitTime: 1000
+  #      watchDelay: 1000
+  exclude:
+    enabled: false
+    paths:
+      - /favicon.ico
+  fallback:
+    enabled: false
+    paths:
+      - /fallback/hystrix
+      - /fallback/resilience4j
+      - /fallback/sentinel
+  health:
+    enabled: true
+    paths:
+      - /actuator
+      - /health_check
+  extPlugin:
+    path:
+    enabled: true
+    threads: 1
+    scheduleTime: 300
+    scheduleDelay: 30
+  scheduler:
+    enabled: false
+    type: fixed
+    threads: 16
+  upstreamCheck:
+    enabled: false
+    poolSize: 10
+    timeout: 3000
+    healthyThreshold: 1
+    unhealthyThreshold: 1
+    interval: 5000
+    printEnabled: true
+    printInterval: 60000
+  springCloudCache:
+    enabled: false
+  ribbon:
+    serverListRefreshInterval: 10000
+  metrics:
+    enabled: false
+    name : prometheus
+    host: 127.0.0.1
+    port: 8090
+    jmxConfig:
+    props:
+      jvm_enabled: true
+  #  plugins:
+  #    rate-limiter.enabled: false
+  local:
+    enabled: false
+    sha512Key: 
"BA3253876AED6BC22D4A6FF53D8406C6AD864195ED144AB5C87621B6C233B548BAEAE6956DF346EC8C17F5EA10F35EE3CBC514797ED7DDD3145464E2A0BAB413"
+#  sharedPool:
+#    enable: true
+#    prefix: "shenyu-shared"
+#    corePoolSize: 200
+#    maximumPoolSize: 2000
+#    keepAliveTime: 60000
+#    # 1GB
+#    maxWorkQueueMemory: 1073741824
+#    # 256MB
+#    maxFreeMemory: 268435456
+
+logging:
+  level:
+    root: info
+    org.springframework.boot: info
+    org.apache.ibatis: info
+    org.apache.shenyu.bonuspoint: info
+    org.apache.shenyu.lottery: info
+    org.apache.shenyu: info
diff --git 
a/shenyu-e2e/shenyu-e2e-case/shenyu-e2e-case-grpc/src/test/resources/docker-compose.mysql.yml
 
b/shenyu-e2e/shenyu-e2e-case/shenyu-e2e-case-grpc/src/test/resources/docker-compose.mysql.yml
new file mode 100644
index 0000000000..da6df2b9d9
--- /dev/null
+++ 
b/shenyu-e2e/shenyu-e2e-case/shenyu-e2e-case-grpc/src/test/resources/docker-compose.mysql.yml
@@ -0,0 +1,126 @@
+#
+# 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.
+#
+
+version: '2.3'
+
+services:
+  zookeeper:
+    image: zookeeper:3.8.0
+    restart: always
+    expose:
+      - 2181
+    healthcheck:
+      test: ["CMD", "curl", "-f", "http://localhost:8080/commands/stat";]
+      interval: 10s
+      timeout: 5s
+      retries: 3
+      start_period: 40s
+
+  nacos:
+    image: nacos/nacos-server:v2.2.3
+    environment:
+      - TZ=Asia/Shanghai
+      - MODE=standalone
+    expose:
+      - 8848
+      - 9848
+      - 9849
+    healthcheck:
+      test: ["CMD", "curl", "-f", 
"http://localhost:8848/nacos/actuator/health";]
+      interval: 10s
+      timeout: 5s
+      retries: 3
+      start_period: 30s
+
+  admin:
+    image: shenyu/admin:latest
+    expose:
+      - 9095
+    ports:
+      - "9095:9095"
+    depends_on:
+      zookeeper:
+        condition: service_started
+      nacos:
+        condition: service_started
+      mysql:
+        condition: service_started
+    volumes:
+      - 
../../target/test-classes/admin-application.yml:/opt/shenyu-admin/conf/application.yml
+      - 
/tmp/shenyu-e2e/mysql/mysql-connector.jar:/opt/shenyu-admin/ext-lib/mysql-connector.jar
+    environment:
+      - SPRING_PROFILES_ACTIVE=mysql
+      - spring.datasource.username=shenyue2e
+      - spring.datasource.password=123456
+      - 
spring.datasource.url=jdbc:mysql://mysql:3306/shenyu?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai&zeroDateTimeBehavior=convertToNull
+    healthcheck:
+      test: [ "CMD-SHELL", "wget -q -O - http://admin:9095/actuator/health | 
grep UP || exit 1" ]
+      timeout: 2s
+      retries: 3
+      start_period: 10s
+
+  grpc:
+    image: shenyu-examples-grpc:latest
+    restart: always
+    environment:
+      - shenyu.register.serverLists=http://admin:9095
+    healthcheck:
+      test: ["CMD", "nc", "-z", "localhost", "55290"]
+      timeout: 2s
+      retries: 3
+      start_period: 10s
+    expose:
+      - 55290
+      - 8080
+    ports:
+      - "55290:55290"
+      - "8080:8080"
+    depends_on:
+      gateway:
+        condition: service_healthy
+
+  gateway:
+    image: shenyu/bootstrap:latest
+    expose:
+      - 9195
+    ports:
+      - "9195:9195"
+    depends_on:
+      admin:
+        condition: service_healthy
+    volumes:
+      - 
../../target/test-classes/bootstrap-application.yml:/opt/shenyu-bootstrap/conf/application.yml
+    healthcheck:
+      test: [ "CMD", "wget", "-q", "-O", "-", 
"http://gateway:9195/actuator/health"; ]
+      timeout: 2s
+      retries: 3
+      start_period: 5s
+
+  mysql:
+    image: mysql:8
+    environment:
+      - MYSQL_ROOT_PASSWORD=123123
+      - MYSQL_USER=shenyue2e
+      - MYSQL_PASSWORD=123456
+    healthcheck:
+      test: "/usr/bin/mysql --user=root --password=123123 --execute \"SHOW 
DATABASES;\""
+      interval: 2s
+      timeout: 20s
+      retries: 10
+      start_period: 60s
+    volumes:
+      - /tmp/shenyu-e2e/mysql/schema.sql:/docker-entrypoint-initdb.d/schema.sql
\ No newline at end of file
diff --git 
a/shenyu-e2e/shenyu-e2e-client/src/main/java/org/apache/shenyu/e2e/client/admin/AdminClient.java
 
b/shenyu-e2e/shenyu-e2e-client/src/main/java/org/apache/shenyu/e2e/client/admin/AdminClient.java
index d3ffc4fc49..eb3684046c 100644
--- 
a/shenyu-e2e/shenyu-e2e-client/src/main/java/org/apache/shenyu/e2e/client/admin/AdminClient.java
+++ 
b/shenyu-e2e/shenyu-e2e-client/src/main/java/org/apache/shenyu/e2e/client/admin/AdminClient.java
@@ -17,6 +17,7 @@
 
 package org.apache.shenyu.e2e.client.admin;
 
+import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.core.type.TypeReference;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.google.common.base.Function;
@@ -54,6 +55,7 @@ import org.springframework.http.HttpHeaders;
 import org.springframework.http.HttpMethod;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
+import org.springframework.http.MediaType;
 import org.springframework.util.MultiValueMap;
 import org.springframework.web.client.RestTemplate;
 
@@ -477,9 +479,35 @@ public class AdminClient {
         Assertions.assertNotNull(rst, "checking http response body");
         return Assertions.assertDoesNotThrow(() -> rst.toObject(valueType), 
"checking cast data to " + valueType.getSimpleName());
     }
-    
+
+    /**
+     * change plugin status.
+     *
+     * @param id id
+     * @param selectorData selectorData
+     */
+    public void changeSelector(final String id, final SelectorData 
selectorData) {
+        ObjectMapper mapper = new ObjectMapper();
+        try {
+            putResourceByJson("/selector", id, 
mapper.writeValueAsString(selectorData));
+        } catch (JsonProcessingException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private void putResourceByJson(final String uri, final String id, final 
String json) {
+        basicAuth.add("Content-Type", MediaType.APPLICATION_JSON_VALUE);
+        HttpEntity<String> requestEntity = new HttpEntity<>(json, basicAuth);
+        ResponseEntity<ShenYuResult> response = template.exchange(baseURL + 
uri + "/" + id, HttpMethod.PUT, requestEntity, ShenYuResult.class);
+        Assertions.assertEquals(HttpStatus.OK, response.getStatusCode(), 
"checking http status");
+        ShenYuResult rst = response.getBody();
+        Assertions.assertNotNull(rst, "checking http response body");
+        basicAuth.remove("Content-Type");
+    }
+
     @FunctionalInterface
     interface Mapper<I, O> extends Function<I, O> {
     
     }
+    
 }
diff --git 
a/shenyu-e2e/shenyu-e2e-common/src/main/java/org/apache/shenyu/e2e/model/data/Condition.java
 
b/shenyu-e2e/shenyu-e2e-common/src/main/java/org/apache/shenyu/e2e/model/data/Condition.java
index 2b27121303..06727a6762 100644
--- 
a/shenyu-e2e/shenyu-e2e-common/src/main/java/org/apache/shenyu/e2e/model/data/Condition.java
+++ 
b/shenyu-e2e/shenyu-e2e-common/src/main/java/org/apache/shenyu/e2e/model/data/Condition.java
@@ -17,24 +17,128 @@
 
 package org.apache.shenyu.e2e.model.data;
 
+import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fasterxml.jackson.annotation.JsonValue;
 import org.jetbrains.annotations.NotNull;
 
+import java.util.Date;
+
 /**
  * The request condition.
  */
 public final class Condition {
 
+    /**
+     * primary key.
+     */
+    private String id;
+
+    /**
+     * selector id.
+     */
+    private String selectorId;
+
+    /**
+     * The type of param.
+     */
     @NotNull
     private ParamType paramType;
-    
+
+    /**
+     * The type of param.
+     */
+    private String paramTypeName;
+
+    /**
+     * The type of operator.
+     */
     @NotNull
     private Operator operator;
-    
+
+    /**
+     * The name of param.
+     */
     private String paramName;
-    
+
+    /**
+     * The value of param.
+     */
     private String paramValue;
 
+    /**
+     * The name of operator.
+     */
+    private String operatorName;
+
+    /**
+     * dateCreated.
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date dateCreated;
+
+    /**
+     * dateUpdated.
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date dateUpdated;
+
+    /**
+     * no args constructor.
+     */
+    public Condition() {
+    }
+
+    /**
+     * builder constructor.
+     *
+     * @param builder builder
+     */
+    private Condition(final Builder builder) {
+        this.paramType = builder.paramType;
+        this.operator = builder.operator;
+        this.paramName = builder.paramName;
+        this.paramValue = builder.paramValue;
+        this.id = builder.id;
+        this.selectorId = builder.selectorId;
+        this.paramTypeName = builder.paramTypeName;
+        this.operatorName = builder.operatorName;
+        this.dateCreated = builder.dateCreated;
+        this.dateUpdated = builder.dateUpdated;
+    }
+
+    /**
+     * get paramType.
+     *
+     * @return paramType
+     */
+    public Date getDateCreated() {
+        return dateCreated;
+    }
+
+    /**
+     * set paramType.
+     * @param dateCreated dateCreated
+     */
+    public void setDateCreated(final Date dateCreated) {
+        this.dateCreated = dateCreated;
+    }
+
+    /**
+     * get paramType.
+     * @return paramType
+     */
+    public Date getDateUpdated() {
+        return dateUpdated;
+    }
+
+    /**
+     * set paramType.
+     * @param dateUpdated dateUpdated
+     */
+    public void setDateUpdated(final Date dateUpdated) {
+        this.dateUpdated = dateUpdated;
+    }
+
     /**
      * The type of param.
      */
@@ -54,13 +158,13 @@ public final class Condition {
         HEADER("header"),
 
         COOKIE("cookie"),
-    
+
         IP("ip"),
 
         HOST("host"),
 
         DOMAIN("domain");
-    
+
         private final String alias;
 
         /**
@@ -82,7 +186,7 @@ public final class Condition {
             return alias;
         }
     }
-    
+
     public enum Operator {
 
         MATCH("match"),
@@ -127,17 +231,7 @@ public final class Condition {
         }
     }
 
-    /**
-     * builder constructor.
-     *
-     * @param builder builder
-     */
-    private Condition(final Builder builder) {
-        this.paramType = builder.paramType;
-        this.operator = builder.operator;
-        this.paramName = builder.paramName;
-        this.paramValue = builder.paramValue;
-    }
+
 
     /**
      * class builder.
@@ -220,21 +314,130 @@ public final class Condition {
         return paramValue;
     }
 
+    /**
+     * get id.
+     * @return id
+     */
+    public String getId() {
+        return id;
+    }
+
+    /**
+     * set id.
+     * @param id id
+     */
+    public void setId(final String id) {
+        this.id = id;
+    }
+
+    /**
+     * get selectorId.
+     * @return selectorId
+     */
+    public String getSelectorId() {
+        return selectorId;
+    }
+
+    /**
+     * set selectorId.
+     * @param selectorId selectorId
+     */
+    public void setSelectorId(final String selectorId) {
+        this.selectorId = selectorId;
+    }
+
+    /**
+     * get paramTypeName.
+     * @return paramTypeName
+     */
+    public String getParamTypeName() {
+        return paramTypeName;
+    }
+
+    /**
+     * set paramTypeName.
+     * @param paramTypeName paramTypeName
+     */
+    public void setParamTypeName(final String paramTypeName) {
+        this.paramTypeName = paramTypeName;
+    }
+
+    /**
+     * get operatorName.
+     * @return operatorName
+     */
+    public String getOperatorName() {
+        return operatorName;
+    }
+
+    /**
+     * set operatorName.
+     * @param operatorName operatorName
+     */
+    public void setOperatorName(final String operatorName) {
+        this.operatorName = operatorName;
+    }
+
     /**
      * class builder.
      */
     public static final class Builder {
 
+
+        /**
+         * primary key.
+         */
+        private String id;
+
+        /**
+         * selector id.
+         */
+        private String selectorId;
+
+        /**
+         * The type of param.
+         */
+        private String paramTypeName;
+
+        /**
+         * The type of param.
+         */
         @NotNull
         private ParamType paramType;
 
+        /**
+         * The type of operator.
+         */
         @NotNull
         private Operator operator;
 
+        /**
+         * The name of param.
+         */
         private String paramName;
 
+        /**
+         * The value of param.
+         */
         private String paramValue;
 
+        /**
+         * The name of operator.
+         */
+        private String operatorName;
+
+        /**
+         * dateCreated.
+         */
+        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS")
+        private Date dateCreated;
+
+        /**
+         * dateUpdated.
+         */
+        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS")
+        private Date dateUpdated;
+
         /**
          * no args constructor.
          */
@@ -284,14 +487,80 @@ public final class Condition {
         }
 
         /**
-         * build paramValue.
+         * build paramName.
          *
          * @param paramValue paramValue
-         * @return paramValue
+         * @return this
          */
         public Builder paramValue(final String paramValue) {
             this.paramValue = paramValue;
             return this;
         }
+
+        /**
+         * build paramValue.
+         *
+         * @param id id
+         * @return id
+         */
+        public Builder id(final String id) {
+            this.id = id;
+            return this;
+        }
+
+        /**
+         * build selectorId.
+         *
+         * @param selectorId selectorId
+         * @return selectorId
+         */
+        public Builder selectorId(final String selectorId) {
+            this.selectorId = selectorId;
+            return this;
+        }
+
+        /**
+         * build paramTypeName.
+         *
+         * @param paramTypeName paramTypeName
+         * @return paramTypeName
+         */
+        public Builder paramTypeName(final String paramTypeName) {
+            this.paramTypeName = paramTypeName;
+            return this;
+        }
+
+        /**
+         * build operatorName.
+         *
+         * @param operatorName operatorName
+         * @return operatorName
+         */
+        public Builder operatorName(final String operatorName) {
+            this.operatorName = operatorName;
+            return this;
+        }
+
+        /**
+         * build operatorName.
+         *
+         * @param dateCreated dateCreated
+         * @return dateCreated
+         */
+        public Builder dateCreated(final Date dateCreated) {
+            this.dateCreated = dateCreated;
+            return this;
+        }
+
+        /**
+         * build operatorName.
+         *
+         * @param dateUpdated dateUpdated
+         * @return dateUpdated
+         */
+        public Builder dateUpdated(final Date dateUpdated) {
+            this.dateUpdated = dateUpdated;
+            return this;
+        }
     }
 }
diff --git 
a/shenyu-e2e/shenyu-e2e-common/src/main/java/org/apache/shenyu/e2e/model/handle/GrpcSelectorHandle.java
 
b/shenyu-e2e/shenyu-e2e-common/src/main/java/org/apache/shenyu/e2e/model/handle/GrpcSelectorHandle.java
new file mode 100644
index 0000000000..619fb1dd74
--- /dev/null
+++ 
b/shenyu-e2e/shenyu-e2e-common/src/main/java/org/apache/shenyu/e2e/model/handle/GrpcSelectorHandle.java
@@ -0,0 +1,219 @@
+/*
+ * 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.e2e.model.handle;
+
+
+/**
+ * SpringCLoud selector handle.
+ */
+public class GrpcSelectorHandle implements PluginHandle {
+
+    /**
+     * this is register eureka serviceId.
+     */
+    private String upstreamUrl;
+
+    /**
+     * weight.
+     */
+    private Integer weight;
+
+    /**
+     * status.
+     */
+    private Boolean status;
+
+    /**
+     * no args constructor.
+     */
+    public GrpcSelectorHandle() {
+    }
+
+    /**
+     * builder constructor.
+     *
+     * @param builder builder
+     */
+    public GrpcSelectorHandle(final GrpcSelectorHandle.Builder builder) {
+        this.upstreamUrl = builder.upstreamUrl;
+        this.weight = builder.weight;
+        this.status = builder.status;
+    }
+
+    /**
+     * builder.
+     *
+     * @return Builder
+     */
+    public static GrpcSelectorHandle.Builder builder() {
+        return new GrpcSelectorHandle.Builder();
+    }
+
+    /**
+     * get upstreamUrl.
+     * @return upstreamUrl
+     */
+    public String getUpstreamUrl() {
+        return upstreamUrl;
+    }
+
+    /**
+     * set upstreamUrl.
+     * @param upstreamUrl upstreamUrl
+     */
+    public void setUpstreamUrl(final String upstreamUrl) {
+        this.upstreamUrl = upstreamUrl;
+    }
+
+    /**
+     * get weight.
+     * @return weight
+     */
+    public Integer getWeight() {
+        return weight;
+    }
+
+    /**
+     * set weight.
+     * @param weight weight
+     */
+    public void setWeight(final Integer weight) {
+        this.weight = weight;
+    }
+
+    /**
+     * get status.
+     * @return status
+     */
+    public Boolean getStatus() {
+        return status;
+    }
+
+    /**
+     * set status.
+     * @param status status
+     */
+    public void setStatus(final Boolean status) {
+        this.status = status;
+    }
+
+    /**
+     * builder class.
+     */
+    public static final class Builder {
+        private String upstreamUrl;
+
+        private Integer weight;
+
+        private Boolean status;
+
+        /**
+         * no args constructor.
+         */
+        private Builder() {
+        }
+
+        /**
+         * build.
+         *
+         * @return SpringCloudRuleHandle
+         */
+        public GrpcSelectorHandle build() {
+            return new GrpcSelectorHandle(this);
+        }
+
+        /**
+         * get upstreamUrl.
+         * @return upstreamUrl
+         */
+        public String getUpstreamUrl() {
+            return upstreamUrl;
+        }
+
+        /**
+         * set upstreamUrl.
+         * @param upstreamUrl upstreamUrl
+         */
+        public void setUpstreamUrl(final String upstreamUrl) {
+            this.upstreamUrl = upstreamUrl;
+        }
+
+        /**
+         * get weight.
+         * @return weight
+         */
+        public Integer getWeight() {
+            return weight;
+        }
+
+        /**
+         *set weight.
+         * @param weight weight
+         */
+        public void setWeight(final Integer weight) {
+            this.weight = weight;
+        }
+
+        /**
+         * get status.
+         * @return status
+         */
+        public Boolean getStatus() {
+            return status;
+        }
+
+        /**
+         * set status.
+         * @param status status
+         */
+        public void setStatus(final Boolean status) {
+            this.status = status;
+        }
+
+        /**
+         * upstreamUrl.
+         * @param upstreamUrl upstreamUrl
+         * @return Builder
+         */
+        public GrpcSelectorHandle.Builder upstreamUrl(final String 
upstreamUrl) {
+            this.upstreamUrl = upstreamUrl;
+            return this;
+        }
+
+        /**
+         * status.
+         * @param status status
+         * @return Builder
+         */
+        public GrpcSelectorHandle.Builder status(final Boolean status) {
+            this.status = status;
+            return this;
+        }
+
+        /**
+         * weight.
+         * @param weight weight
+         * @return Builder
+         */
+        public GrpcSelectorHandle.Builder weight(final Integer weight) {
+            this.weight = weight;
+            return this;
+        }
+
+    }
+}
diff --git 
a/shenyu-e2e/shenyu-e2e-common/src/main/java/org/apache/shenyu/e2e/model/response/SelectorDTO.java
 
b/shenyu-e2e/shenyu-e2e-common/src/main/java/org/apache/shenyu/e2e/model/response/SelectorDTO.java
index 9e0e1ed16f..a89c57cff9 100644
--- 
a/shenyu-e2e/shenyu-e2e-common/src/main/java/org/apache/shenyu/e2e/model/response/SelectorDTO.java
+++ 
b/shenyu-e2e/shenyu-e2e-common/src/main/java/org/apache/shenyu/e2e/model/response/SelectorDTO.java
@@ -42,7 +42,7 @@ public final class SelectorDTO implements ResourceDTO {
     private int type;
 
     private String typeName;
-    
+
     private int sort;
 
     private boolean enabled;
@@ -51,16 +51,16 @@ public final class SelectorDTO implements ResourceDTO {
     private boolean logged;
 
     private boolean continued;
-    
+
     private String handle;
-    
+
     @JsonProperty("selectorConditions")
     private List<Condition> conditionList;
-    
-    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS")
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     private Date dateCreated;
 
-    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     private Date dateUpdated;
 
     private SelectorDTO() {
@@ -84,7 +84,7 @@ public final class SelectorDTO implements ResourceDTO {
         this.dateCreated = builder.dateCreated;
         this.dateUpdated = builder.dateUpdated;
     }
-    
+
     /**
      * builder.
      * @return Builder
@@ -397,16 +397,16 @@ public final class SelectorDTO implements ResourceDTO {
         @JsonProperty("selectorConditions")
         private List<Condition> conditionList;
 
-        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS")
+        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
         private Date dateCreated;
 
-        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS")
+        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
         private Date dateUpdated;
 
         private Builder() {
 
         }
-        
+
         /**
          * build.
          * @return SelectorDTO
@@ -414,7 +414,7 @@ public final class SelectorDTO implements ResourceDTO {
         public SelectorDTO build() {
             return new SelectorDTO(this);
         }
-        
+
         /**
          * id.
          * @param id id
@@ -424,7 +424,7 @@ public final class SelectorDTO implements ResourceDTO {
             this.id = id;
             return this;
         }
-        
+
         /**
          * plugin id.
          * @param pluginId pluginId
@@ -434,7 +434,7 @@ public final class SelectorDTO implements ResourceDTO {
             this.pluginId = pluginId;
             return this;
         }
-        
+
         /**
          * name.
          * @param name name
@@ -444,7 +444,7 @@ public final class SelectorDTO implements ResourceDTO {
             this.name = name;
             return this;
         }
-        
+
         /**
          * match mode.
          * @param matchMode matchMode
@@ -454,7 +454,7 @@ public final class SelectorDTO implements ResourceDTO {
             this.matchMode = matchMode;
             return this;
         }
-        
+
         /**
          * match mode name.
          * @param matchModeName matchModeName
@@ -464,7 +464,7 @@ public final class SelectorDTO implements ResourceDTO {
             this.matchModeName = matchModeName;
             return this;
         }
-        
+
         /**
          * type.
          * @param type type
@@ -474,7 +474,7 @@ public final class SelectorDTO implements ResourceDTO {
             this.type = type;
             return this;
         }
-        
+
         /**
          * type name.
          * @param typeName typeName
@@ -484,7 +484,7 @@ public final class SelectorDTO implements ResourceDTO {
             this.typeName = typeName;
             return this;
         }
-        
+
         /**
          * sort.
          * @param sort sort
diff --git 
a/shenyu-e2e/shenyu-e2e-engine/src/main/java/org/apache/shenyu/e2e/engine/scenario/function/HttpCheckers.java
 
b/shenyu-e2e/shenyu-e2e-engine/src/main/java/org/apache/shenyu/e2e/engine/scenario/function/HttpCheckers.java
index a5abfd95e3..354fe25f0b 100644
--- 
a/shenyu-e2e/shenyu-e2e-engine/src/main/java/org/apache/shenyu/e2e/engine/scenario/function/HttpCheckers.java
+++ 
b/shenyu-e2e/shenyu-e2e-engine/src/main/java/org/apache/shenyu/e2e/engine/scenario/function/HttpCheckers.java
@@ -17,8 +17,12 @@
 
 package org.apache.shenyu.e2e.engine.scenario.function;
 
+import io.restassured.http.Header;
 import io.restassured.http.Method;
 import org.junit.jupiter.api.Assertions;
+import org.springframework.http.HttpStatus;
+
+import java.util.Map;
 
 import static org.hamcrest.Matchers.containsString;
 import static org.hamcrest.Matchers.lessThan;
@@ -91,5 +95,28 @@ public class HttpCheckers {
             }
         };
     }
+
+    /**
+     * Detection endpoint exists.
+     *
+     * @param method method
+     * @param endpoint endpoint
+     * @param body body
+     * @return HttpChecker
+     */
+    public static HttpChecker exists(final Method method, final String 
endpoint, final Map<String, ?> body) {
+        return request -> {
+            try {
+                request.header(new Header("Content-Type", 
"application/json")).body(body).request(method, endpoint)
+                        .then()
+                        .log()
+                        .ifValidationFails()
+                        .statusCode(HttpStatus.OK.value())
+                        .body("message", not(containsString("please check your 
configuration!")));
+            } catch (AssertionError error) {
+                Assertions.fail("endpoint '" + endpoint + "' not exists", 
error);
+            }
+        };
+    }
     
 }
diff --git 
a/shenyu-e2e/shenyu-e2e-engine/src/main/java/org/apache/shenyu/e2e/engine/scenario/specification/ShenYuCaseSpec.java
 
b/shenyu-e2e/shenyu-e2e-engine/src/main/java/org/apache/shenyu/e2e/engine/scenario/specification/ShenYuCaseSpec.java
index 01471032bd..89d138183d 100644
--- 
a/shenyu-e2e/shenyu-e2e-engine/src/main/java/org/apache/shenyu/e2e/engine/scenario/specification/ShenYuCaseSpec.java
+++ 
b/shenyu-e2e/shenyu-e2e-engine/src/main/java/org/apache/shenyu/e2e/engine/scenario/specification/ShenYuCaseSpec.java
@@ -24,6 +24,7 @@ import 
org.apache.shenyu.e2e.engine.scenario.function.Verifier;
 import org.hamcrest.Matcher;
 
 import java.util.List;
+import java.util.Map;
 
 import static 
org.apache.shenyu.e2e.engine.scenario.function.HttpCheckers.exists;
 import static 
org.apache.shenyu.e2e.engine.scenario.function.HttpCheckers.notExists;
@@ -153,7 +154,20 @@ public class ShenYuCaseSpec implements CaseSpec {
         public ShenYuTestCaseSpecBuilder addExists(final Method method, final 
String endpoint) {
             return add(exists(method, endpoint));
         }
-        
+
+
+        /**
+         * add exist method endpoint case spec.
+         * @param method method
+         * @param endpoint endpoint
+         * @param body body
+         * @return ShenYuTestCaseSpecBuilder
+         */
+        public ShenYuTestCaseSpecBuilder addExists(final Method method, final 
String endpoint, final Map<String, ?> body) {
+            return add(exists(method, endpoint, body));
+        }
+
+
         /**
          * add not exist endpoint case spec.
          * @param endpoint endpoint
diff --git 
a/shenyu-examples/shenyu-examples-grpc/src/main/resources/application.yml 
b/shenyu-examples/shenyu-examples-grpc/src/main/resources/application.yml
index 4ff4820e7d..594559eb7b 100644
--- a/shenyu-examples/shenyu-examples-grpc/src/main/resources/application.yml
+++ b/shenyu-examples/shenyu-examples-grpc/src/main/resources/application.yml
@@ -14,7 +14,7 @@
 # limitations under the License.
 
 server:
-  port: 55222
+  port: 55290
   servlet:
     context-path: /grpc
   address: 0.0.0.0


Reply via email to