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