diff --git a/.circleci/config.yml b/.circleci/config.yml
index cf810187..787d875d 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -4,32 +4,32 @@ version: 2.1
setup: true
orbs:
- gravitee: gravitee-io/gravitee@2.1
+ gravitee: gravitee-io/gravitee@4.0.0
# our single workflow, that triggers the setup job defined above, filters on tag and branches are needed otherwise
# some workflow and job will not be triggered for tags (default CircleCI behavior)
workflows:
- setup_build:
- when:
- not: << pipeline.git.tag >>
- jobs:
- - gravitee/setup_plugin-build-config:
- filters:
- tags:
- ignore:
- - /.*/
+ setup_build:
+ when:
+ not: << pipeline.git.tag >>
+ jobs:
+ - gravitee/setup_plugin-build-config:
+ filters:
+ tags:
+ ignore:
+ - /.*/
- setup_release:
- when:
- matches:
- pattern: "^[0-9]+\\.[0-9]+\\.[0-9]+$"
- value: << pipeline.git.tag >>
- jobs:
- - gravitee/setup_plugin-release-config:
- filters:
- branches:
- ignore:
- - /.*/
- tags:
- only:
- - /^[0-9]+\.[0-9]+\.[0-9]+$/
\ No newline at end of file
+ setup_release:
+ when:
+ matches:
+ pattern: "/^[0-9]+\\.[0-9]+\\.[0-9]+(-(alpha|beta|rc)\\.[0-9]+)?$/"
+ value: << pipeline.git.tag >>
+ jobs:
+ - gravitee/setup_plugin-release-config:
+ filters:
+ branches:
+ ignore:
+ - /.*/
+ tags:
+ only:
+ - /^[0-9]+\.[0-9]+\.[0-9]+(-(alpha|beta|rc)\.[0-9]+)?$/
\ No newline at end of file
diff --git a/README.adoc b/README.adoc
index 2f92d2fb..0762ec9d 100644
--- a/README.adoc
+++ b/README.adoc
@@ -7,16 +7,44 @@ image:https://img.shields.io/badge/semantic--release-conventional%20commits-e100
image:https://circleci.com/gh/gravitee-io/gravitee-policy-latency.svg?style=svg["CircleCI", link="https://circleci.com/gh/gravitee-io/gravitee-policy-latency"]
endif::[]
-== Phase
+== Compatibility with APIM
+
+|===
+|Plugin version | APIM version
+
+| 2.x and upper | 4.x and upper
+| 1.4.x | 3.10.x and upper
+| Up to 1.3.x | Up to 3.9.x
+|===
+
+=== V3 engine
-[cols="2*", options="header"]
+[cols="4*", options="header"]
|===
^|onRequest
^|onResponse
+^|onRequestContent
+^|onResponseContent
^.^| X
-^.^|
+^.^|
+^.^|
+^.^|
+|===
+=== V4 engine
+
+[cols="4*", options="header"]
+|===
+^|onRequest
+^|onResponse
+^|onMessageRequest
+^|onMessageResponse
+
+^.^| X
+^.^|
+^.^| X
+^.^| X
|===
== Description
@@ -28,15 +56,6 @@ This policy is particularly useful in two scenarios:
* Testing: adding latency allows you to test client applications when APIs are slow to respond.
* Monetization: a longer latency can be added to free plans to encourage clients to move to a better (or paid) plan.
-== Compatibility with APIM
-
-|===
-|Plugin version | APIM version
-
-| 1.4.x and upper | 3.10.x and upper
-| Up to 1.3.x | Up to 3.9.x
-|===
-
== Configuration
You can configure the policy with the following options:
diff --git a/pom.xml b/pom.xml
index c50c3b7b..51e62263 100644
--- a/pom.xml
+++ b/pom.xml
@@ -22,7 +22,7 @@
io.gravitee.policy
gravitee-policy-latency
- 1.4.1
+ 2.0.0
jar
Gravitee.io APIM - Policy - Latency
Description of the Latency Gravitee Policy
@@ -30,18 +30,27 @@
io.gravitee
gravitee-parent
- 20.2
+ 21.0.1
- 3.18.0-SNAPSHOT
- 2.5
- 1.32.2
+ 5.0.0
+ 3.0.0-alpha.6
+ 3.1.0-alpha.10
1.11.0
+ 2.1.1
+ 4.0.0-SNAPSHOT
+ 3.1.0-alpha.3
+ 1.0.0-alpha.2
+ 1.0.0-alpha.7
+
1.3.0
${project.build.directory}/schemas
- 3.3.0
+ 3.6.0
+ 0.19
+ 1.6.2
+ 1.1.0
graviteeio-apim/plugins/policies
@@ -56,6 +65,16 @@
import
pom
+
+ io.gravitee.policy
+ gravitee-policy-api
+ ${gravitee-policy-api.version}
+
+
+ io.gravitee.gateway
+ gravitee-gateway-api
+ ${gravitee-gateway-api.version}
+
@@ -81,15 +100,86 @@
vertx-core
provided
-
-
+
+ io.vertx
+ vertx-rx-java3
+ provided
+
org.slf4j
slf4j-api
provided
+
+
+ org.projectlombok
+ lombok
+ provided
+
+
+
+ io.gravitee.apim.gateway
+ gravitee-apim-gateway-tests-sdk
+ ${gravitee-apim.version}
+ test
+
+
+ io.gravitee.apim.plugin.entrypoint
+ gravitee-apim-plugin-entrypoint-http-proxy
+ ${gravitee-apim.version}
+ test
+
+
+ io.gravitee.apim.plugin.endpoint
+ gravitee-apim-plugin-endpoint-http-proxy
+ ${gravitee-apim.version}
+ test
+
+
+ com.graviteesource.reactor
+ gravitee-reactor-message
+ ${gravitee-reactor-message.version}
+ test
+
+
+ io.gravitee.apim.plugin.endpoint
+ gravitee-apim-plugin-endpoint-mock
+ ${gravitee-apim.version}
+ test
+
+
+ com.graviteesource.entrypoint
+ gravitee-entrypoint-sse
+ ${gravitee-sse.version}
+ test
+
+
+ com.graviteesource.entrypoint
+ gravitee-entrypoint-http-post
+ ${gravitee-http-post.version}
+ test
+
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ test
+
+
+
+ org.junit.jupiter
+ junit-jupiter-params
+ test
+
+
org.junit.vintage
junit-vintage-engine
@@ -103,9 +193,14 @@
- io.gravitee.apim.gateway
- gravitee-apim-gateway-tests-sdk
- ${gravitee-apim-gateway-tests-sdk.version}
+ org.mockito
+ mockito-junit-jupiter
+ test
+
+
+
+ org.assertj
+ assertj-core
test
@@ -121,7 +216,7 @@
org.codehaus.mojo
properties-maven-plugin
- 1.0.0
+ ${maven-plugin-properties.version}
initialize
@@ -157,9 +252,11 @@
+
+ org.apache.maven.plugins
maven-assembly-plugin
- ${maven-assembly-plugin.version}
+ ${maven-plugin-assembly.version}
false
@@ -179,10 +276,10 @@
com.hubspot.maven.plugins
prettier-maven-plugin
- 0.17
+ ${maven-plugin-prettier.version}
- 12.13.0
- 1.6.1
+ ${maven-plugin-prettier.prettierjava.version}
+ ${skip.validation}
diff --git a/src/main/java/io/gravitee/policy/latency/LatencyPolicy.java b/src/main/java/io/gravitee/policy/latency/LatencyPolicy.java
index 5cbad3ba..7af1c34b 100644
--- a/src/main/java/io/gravitee/policy/latency/LatencyPolicy.java
+++ b/src/main/java/io/gravitee/policy/latency/LatencyPolicy.java
@@ -15,38 +15,41 @@
*/
package io.gravitee.policy.latency;
-import io.gravitee.gateway.api.ExecutionContext;
-import io.gravitee.gateway.api.Request;
-import io.gravitee.gateway.api.Response;
-import io.gravitee.policy.api.PolicyChain;
-import io.gravitee.policy.api.annotations.OnRequest;
+import io.gravitee.gateway.reactive.api.context.HttpExecutionContext;
+import io.gravitee.gateway.reactive.api.context.MessageExecutionContext;
+import io.gravitee.gateway.reactive.api.policy.Policy;
import io.gravitee.policy.latency.configuration.LatencyPolicyConfiguration;
-import io.vertx.core.Vertx;
+import io.gravitee.policy.latency.v3.LatencyPolicyV3;
+import io.reactivex.rxjava3.core.Completable;
+import io.reactivex.rxjava3.core.Maybe;
/**
- * @author Azize ELAMRANI (azize.elamrani at graviteesource.com)
+ * @author Guillaume Lamirand (guillaume.lamirand at graviteesource.com)
* @author GraviteeSource Team
*/
-public class LatencyPolicy {
-
- private final LatencyPolicyConfiguration latencyPolicyConfiguration;
+public class LatencyPolicy extends LatencyPolicyV3 implements Policy {
public LatencyPolicy(final LatencyPolicyConfiguration latencyPolicyConfiguration) {
- this.latencyPolicyConfiguration = latencyPolicyConfiguration;
+ super(latencyPolicyConfiguration);
+ }
+
+ @Override
+ public String id() {
+ return "latency";
+ }
+
+ @Override
+ public Completable onRequest(final HttpExecutionContext ctx) {
+ return Completable.complete().delay(configuration.getTime(), configuration.getTimeUnit());
+ }
+
+ @Override
+ public Completable onMessageRequest(final MessageExecutionContext ctx) {
+ return ctx.request().onMessage(message -> Maybe.just(message).delay(configuration.getTime(), configuration.getTimeUnit()));
}
- @OnRequest
- public void onRequest(
- final Request request,
- final Response response,
- final ExecutionContext executionContext,
- final PolicyChain policyChain
- ) {
- executionContext
- .getComponent(Vertx.class)
- .setTimer(
- latencyPolicyConfiguration.getTimeUnit().toMillis(latencyPolicyConfiguration.getTime()),
- timerId -> policyChain.doNext(request, response)
- );
+ @Override
+ public Completable onMessageResponse(final MessageExecutionContext ctx) {
+ return ctx.response().onMessage(message -> Maybe.just(message).delay(configuration.getTime(), configuration.getTimeUnit()));
}
}
diff --git a/src/main/java/io/gravitee/policy/latency/configuration/LatencyPolicyConfiguration.java b/src/main/java/io/gravitee/policy/latency/configuration/LatencyPolicyConfiguration.java
index 1d9fe0f1..e7937094 100644
--- a/src/main/java/io/gravitee/policy/latency/configuration/LatencyPolicyConfiguration.java
+++ b/src/main/java/io/gravitee/policy/latency/configuration/LatencyPolicyConfiguration.java
@@ -17,29 +17,23 @@
import io.gravitee.policy.api.PolicyConfiguration;
import java.util.concurrent.TimeUnit;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
/**
* @author Azize ELAMRANI (azize.elamrani at graviteesource.com)
* @author GraviteeSource Team
*/
+@Getter
+@Setter
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
public class LatencyPolicyConfiguration implements PolicyConfiguration {
private long time;
private TimeUnit timeUnit;
-
- public long getTime() {
- return time;
- }
-
- public void setTime(long time) {
- this.time = time;
- }
-
- public TimeUnit getTimeUnit() {
- return timeUnit;
- }
-
- public void setTimeUnit(TimeUnit timeUnit) {
- this.timeUnit = timeUnit;
- }
}
diff --git a/src/main/java/io/gravitee/policy/latency/v3/LatencyPolicyV3.java b/src/main/java/io/gravitee/policy/latency/v3/LatencyPolicyV3.java
new file mode 100644
index 00000000..7c553f54
--- /dev/null
+++ b/src/main/java/io/gravitee/policy/latency/v3/LatencyPolicyV3.java
@@ -0,0 +1,47 @@
+/**
+ * Copyright (C) 2015 The Gravitee team (http://gravitee.io)
+ *
+ * Licensed 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 io.gravitee.policy.latency.v3;
+
+import io.gravitee.gateway.api.ExecutionContext;
+import io.gravitee.gateway.api.Request;
+import io.gravitee.gateway.api.Response;
+import io.gravitee.policy.api.PolicyChain;
+import io.gravitee.policy.api.annotations.OnRequest;
+import io.gravitee.policy.latency.configuration.LatencyPolicyConfiguration;
+import io.vertx.core.Vertx;
+import lombok.RequiredArgsConstructor;
+
+/**
+ * @author Azize ELAMRANI (azize.elamrani at graviteesource.com)
+ * @author GraviteeSource Team
+ */
+@RequiredArgsConstructor
+public class LatencyPolicyV3 {
+
+ protected final LatencyPolicyConfiguration configuration;
+
+ @OnRequest
+ public void onRequest(
+ final Request request,
+ final Response response,
+ final ExecutionContext executionContext,
+ final PolicyChain policyChain
+ ) {
+ executionContext
+ .getComponent(Vertx.class)
+ .setTimer(configuration.getTimeUnit().toMillis(configuration.getTime()), timerId -> policyChain.doNext(request, response));
+ }
+}
diff --git a/src/main/resources/plugin.properties b/src/main/resources/plugin.properties
index 900b258b..dbb702e0 100644
--- a/src/main/resources/plugin.properties
+++ b/src/main/resources/plugin.properties
@@ -6,3 +6,5 @@ class=io.gravitee.policy.latency.LatencyPolicy
type=policy
category=others
icon=latency.svg
+proxy=REQUEST
+message=REQUEST,MESSAGE_REQUEST,MESSAGE_RESPONSE
diff --git a/src/main/resources/schemas/schema-form.json b/src/main/resources/schemas/schema-form.json
index 755ff502..f3932340 100644
--- a/src/main/resources/schemas/schema-form.json
+++ b/src/main/resources/schemas/schema-form.json
@@ -5,7 +5,8 @@
"time" : {
"title": "Time duration",
"type" : "integer",
- "default": 100
+ "default": 100,
+ "minimum": 0
},
"timeUnit" : {
"title": "Time unit",
diff --git a/src/test/java/io/gravitee/policy/latency/LatencyPolicyIntegrationTest.java b/src/test/java/io/gravitee/policy/latency/LatencyPolicyIntegrationV3Test.java
similarity index 55%
rename from src/test/java/io/gravitee/policy/latency/LatencyPolicyIntegrationTest.java
rename to src/test/java/io/gravitee/policy/latency/LatencyPolicyIntegrationV3Test.java
index 388bcaba..5b989d9a 100644
--- a/src/test/java/io/gravitee/policy/latency/LatencyPolicyIntegrationTest.java
+++ b/src/test/java/io/gravitee/policy/latency/LatencyPolicyIntegrationV3Test.java
@@ -25,13 +25,20 @@
import io.gravitee.apim.gateway.tests.sdk.AbstractPolicyTest;
import io.gravitee.apim.gateway.tests.sdk.annotations.DeployApi;
import io.gravitee.apim.gateway.tests.sdk.annotations.GatewayTest;
+import io.gravitee.apim.gateway.tests.sdk.configuration.GatewayConfigurationBuilder;
import io.gravitee.apim.gateway.tests.sdk.reporter.FakeReporter;
+import io.gravitee.definition.model.Api;
+import io.gravitee.definition.model.ExecutionMode;
import io.gravitee.policy.latency.configuration.LatencyPolicyConfiguration;
import io.gravitee.reporter.api.http.Metrics;
-import io.reactivex.observers.TestObserver;
-import io.vertx.reactivex.core.buffer.Buffer;
-import io.vertx.reactivex.ext.web.client.HttpResponse;
-import io.vertx.reactivex.ext.web.client.WebClient;
+import io.reactivex.rxjava3.observers.TestObserver;
+import io.vertx.core.http.HttpMethod;
+import io.vertx.junit5.Checkpoint;
+import io.vertx.junit5.VertxTestContext;
+import io.vertx.rxjava3.core.buffer.Buffer;
+import io.vertx.rxjava3.core.http.HttpClient;
+import io.vertx.rxjava3.core.http.HttpClientRequest;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
@@ -41,30 +48,52 @@
* @author GraviteeSource Team
*/
@GatewayTest
-@DeployApi("/apis/latency.json")
-class LatencyPolicyIntegrationTest extends AbstractPolicyTest {
+@DeployApi("/apis/latency-v2.json")
+class LatencyPolicyIntegrationV3Test extends AbstractPolicyTest {
+
+ @Override
+ protected void configureGateway(GatewayConfigurationBuilder gatewayConfigurationBuilder) {
+ super.configureGateway(gatewayConfigurationBuilder);
+ gatewayConfigurationBuilder.set("api.jupiterMode.enabled", "false");
+ }
+
+ @Override
+ public void configureApi(Api api) {
+ super.configureApi(api);
+ api.setExecutionMode(ExecutionMode.V3);
+ }
@Test
- @DisplayName("Should apply latency")
- void shouldApplyLatency(WebClient webClient) {
+ void should_apply_latency_on_request(HttpClient httpClient, VertxTestContext vertxTestContext) throws InterruptedException {
+ Checkpoint fakeReporterCheckpoint = vertxTestContext.checkpoint();
FakeReporter fakeReporter = getBean(FakeReporter.class);
AtomicReference metricsRef = new AtomicReference<>();
fakeReporter.setReportableHandler(reportable -> {
+ fakeReporterCheckpoint.flag();
metricsRef.set((Metrics) reportable);
});
wiremock.stubFor(get("/endpoint").willReturn(ok("I'm the backend")));
- final TestObserver> obs = webClient.get("/test").rxSend().test();
- awaitTerminalEvent(obs);
- obs
- .assertComplete()
- .assertValue(response -> {
+ Checkpoint responseCheckpoint = vertxTestContext.checkpoint();
+ TestObserver testObserver = httpClient
+ .request(HttpMethod.GET, "/test")
+ .flatMap(HttpClientRequest::rxSend)
+ .flatMap(response -> {
assertThat(response.statusCode()).isEqualTo(200);
- assertThat(response.bodyAsString()).isEqualTo("I'm the backend");
+ return response.rxBody();
+ })
+ .test();
+ awaitTerminalEvent(testObserver);
+ testObserver
+ .assertComplete()
+ .assertValue(body -> {
+ responseCheckpoint.flag();
+ assertThat(body).hasToString("I'm the backend");
return true;
});
+ assertThat(vertxTestContext.awaitCompletion(30, TimeUnit.SECONDS)).isTrue();
wiremock.verify(exactly(1), getRequestedFor(urlPathEqualTo("/endpoint")));
assertThat(metricsRef.get().getProxyResponseTimeMs()).isGreaterThan(2000);
}
diff --git a/src/test/java/io/gravitee/policy/latency/LatencyPolicyIntegrationV4CompatibilityTest.java b/src/test/java/io/gravitee/policy/latency/LatencyPolicyIntegrationV4CompatibilityTest.java
new file mode 100644
index 00000000..2adca5ee
--- /dev/null
+++ b/src/test/java/io/gravitee/policy/latency/LatencyPolicyIntegrationV4CompatibilityTest.java
@@ -0,0 +1,41 @@
+/**
+ * Copyright (C) 2015 The Gravitee team (http://gravitee.io)
+ *
+ * Licensed 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 io.gravitee.policy.latency;
+
+import io.gravitee.apim.gateway.tests.sdk.annotations.DeployApi;
+import io.gravitee.apim.gateway.tests.sdk.annotations.GatewayTest;
+import io.gravitee.apim.gateway.tests.sdk.configuration.GatewayConfigurationBuilder;
+import io.gravitee.definition.model.Api;
+import io.gravitee.definition.model.ExecutionMode;
+
+/**
+ * @author Guillaume Lamirand (guillaume.lamirand at graviteesource.com)
+ * @author GraviteeSource Team
+ */
+class LatencyPolicyIntegrationV4CompatibilityTest extends LatencyPolicyIntegrationV3Test {
+
+ @Override
+ protected void configureGateway(GatewayConfigurationBuilder gatewayConfigurationBuilder) {
+ super.configureGateway(gatewayConfigurationBuilder);
+ gatewayConfigurationBuilder.set("api.jupiterMode.enabled", "true");
+ }
+
+ @Override
+ public void configureApi(Api api) {
+ super.configureApi(api);
+ api.setExecutionMode(ExecutionMode.JUPITER);
+ }
+}
diff --git a/src/test/java/io/gravitee/policy/latency/LatencyPolicyIntegrationV4Test.java b/src/test/java/io/gravitee/policy/latency/LatencyPolicyIntegrationV4Test.java
new file mode 100644
index 00000000..5fe5e9ba
--- /dev/null
+++ b/src/test/java/io/gravitee/policy/latency/LatencyPolicyIntegrationV4Test.java
@@ -0,0 +1,206 @@
+/**
+ * Copyright (C) 2015 The Gravitee team (http://gravitee.io)
+ *
+ * Licensed 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 io.gravitee.policy.latency;
+
+import static com.github.tomakehurst.wiremock.client.WireMock.exactly;
+import static com.github.tomakehurst.wiremock.client.WireMock.get;
+import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor;
+import static com.github.tomakehurst.wiremock.client.WireMock.ok;
+import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo;
+import static io.vertx.core.http.HttpMethod.POST;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import com.graviteesource.entrypoint.http.post.HttpPostEntrypointConnectorFactory;
+import com.graviteesource.entrypoint.sse.SseEntrypointConnectorFactory;
+import com.graviteesource.reactor.message.MessageApiReactorFactory;
+import io.gravitee.apim.gateway.tests.sdk.AbstractPolicyTest;
+import io.gravitee.apim.gateway.tests.sdk.annotations.DeployApi;
+import io.gravitee.apim.gateway.tests.sdk.annotations.GatewayTest;
+import io.gravitee.apim.gateway.tests.sdk.configuration.GatewayConfigurationBuilder;
+import io.gravitee.apim.gateway.tests.sdk.connector.EndpointBuilder;
+import io.gravitee.apim.gateway.tests.sdk.connector.EntrypointBuilder;
+import io.gravitee.apim.gateway.tests.sdk.connector.fakes.PersistentMockEndpointConnectorFactory;
+import io.gravitee.apim.gateway.tests.sdk.reactor.ReactorBuilder;
+import io.gravitee.apim.gateway.tests.sdk.reporter.FakeReporter;
+import io.gravitee.apim.plugin.reactor.ReactorPlugin;
+import io.gravitee.common.http.MediaType;
+import io.gravitee.gateway.api.http.HttpHeaderNames;
+import io.gravitee.gateway.reactive.reactor.v4.reactor.ReactorFactory;
+import io.gravitee.plugin.endpoint.EndpointConnectorPlugin;
+import io.gravitee.plugin.endpoint.http.proxy.HttpProxyEndpointConnectorFactory;
+import io.gravitee.plugin.entrypoint.EntrypointConnectorPlugin;
+import io.gravitee.plugin.entrypoint.http.proxy.HttpProxyEntrypointConnectorFactory;
+import io.gravitee.policy.latency.configuration.LatencyPolicyConfiguration;
+import io.gravitee.reporter.api.v4.metric.MessageMetrics;
+import io.gravitee.reporter.api.v4.metric.Metrics;
+import io.reactivex.rxjava3.observers.TestObserver;
+import io.vertx.core.http.HttpMethod;
+import io.vertx.junit5.Checkpoint;
+import io.vertx.junit5.VertxExtension;
+import io.vertx.junit5.VertxTestContext;
+import io.vertx.rxjava3.core.buffer.Buffer;
+import io.vertx.rxjava3.core.http.HttpClient;
+import io.vertx.rxjava3.core.http.HttpClientRequest;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+/**
+ * @author Guillaume Lamirand (guillaume.lamirand at graviteesource.com)
+ * @author GraviteeSource Team
+ */
+@GatewayTest
+@ExtendWith(VertxExtension.class)
+class LatencyPolicyIntegrationV4Test extends AbstractPolicyTest {
+
+ private FakeReporter fakeReporter;
+ private AtomicReference metricsRef;
+ private AtomicReference messageMetricsRef;
+
+ @AfterEach
+ void afterEach() {
+ fakeReporter.reset();
+ }
+
+ @Override
+ protected void configureGateway(GatewayConfigurationBuilder gatewayConfigurationBuilder) {
+ super.configureGateway(gatewayConfigurationBuilder);
+ gatewayConfigurationBuilder.set("api.jupiterMode.enabled", "true");
+ }
+
+ @Override
+ public void configureReactors(Set>> reactors) {
+ reactors.add(ReactorBuilder.build(MessageApiReactorFactory.class));
+ }
+
+ @Override
+ public void configureEntrypoints(Map> entrypoints) {
+ entrypoints.putIfAbsent("http-proxy", EntrypointBuilder.build("http-proxy", HttpProxyEntrypointConnectorFactory.class));
+ entrypoints.putIfAbsent("sse", EntrypointBuilder.build("sse", SseEntrypointConnectorFactory.class));
+ entrypoints.putIfAbsent("http-post", EntrypointBuilder.build("http-post", HttpPostEntrypointConnectorFactory.class));
+ }
+
+ @Override
+ public void configureEndpoints(Map> endpoints) {
+ endpoints.putIfAbsent("http-proxy", EndpointBuilder.build("http-proxy", HttpProxyEndpointConnectorFactory.class));
+ endpoints.putIfAbsent("mock", EndpointBuilder.build("mock", PersistentMockEndpointConnectorFactory.class));
+ }
+
+ @Test
+ @DeployApi("/apis/latency-v4-proxy.json")
+ void should_apply_latency_on_proxy_request(HttpClient httpClient, VertxTestContext vertxTestContext) throws InterruptedException {
+ prepareReporter(vertxTestContext, 1);
+ Checkpoint responseCheckpoint = vertxTestContext.checkpoint();
+ wiremock.stubFor(get("/endpoint").willReturn(ok("I'm the backend")));
+
+ TestObserver testObserver = httpClient
+ .request(HttpMethod.GET, "/test")
+ .flatMap(HttpClientRequest::rxSend)
+ .flatMap(response -> {
+ assertThat(response.statusCode()).isEqualTo(200);
+ return response.rxBody();
+ })
+ .test();
+ awaitTerminalEvent(testObserver);
+ testObserver
+ .assertComplete()
+ .assertValue(body -> {
+ responseCheckpoint.flag();
+ assertThat(body).hasToString("I'm the backend");
+ return true;
+ });
+
+ wiremock.verify(exactly(1), getRequestedFor(urlPathEqualTo("/endpoint")));
+
+ assertThat(vertxTestContext.awaitCompletion(30, TimeUnit.SECONDS)).isTrue();
+ Metrics metrics = metricsRef.get();
+ assertThat(metrics).isNotNull();
+ assertThat(metrics.getGatewayResponseTimeMs()).isGreaterThan(2000);
+ }
+
+ @Test
+ @DeployApi("/apis/latency-v4-message-publish.json")
+ void should_apply_latency_on_request_message(HttpClient httpClient, VertxTestContext vertxTestContext) throws InterruptedException {
+ prepareReporter(vertxTestContext, 3);
+ Checkpoint responseCheckpoint = vertxTestContext.checkpoint();
+ httpClient
+ .rxRequest(POST, "/test")
+ .flatMap(request -> request.rxSend("message"))
+ .flatMap(response -> {
+ assertThat(response.statusCode()).isEqualTo(202);
+ return response.body();
+ })
+ .test()
+ .awaitDone(5, TimeUnit.SECONDS)
+ .assertValue(body -> {
+ responseCheckpoint.flag();
+ assertThat(body.length()).isZero();
+ return true;
+ });
+ assertThat(vertxTestContext.awaitCompletion(30, TimeUnit.SECONDS)).isTrue();
+
+ MessageMetrics messageMetrics = messageMetricsRef.get();
+ assertThat(messageMetrics).isNotNull();
+ assertThat(messageMetrics.getGatewayLatencyMs()).isGreaterThan(2000);
+ }
+
+ @Test
+ @DeployApi("/apis/latency-v4-message-subscribe.json")
+ void should_apply_latency_on_response_message(HttpClient httpClient, VertxTestContext vertxTestContext) throws InterruptedException {
+ prepareReporter(vertxTestContext, 3);
+ Checkpoint responseCheckpoint = vertxTestContext.checkpoint();
+ httpClient
+ .rxRequest(HttpMethod.GET, "/test")
+ .flatMap(request -> {
+ request.putHeader(HttpHeaderNames.ACCEPT.toString(), MediaType.TEXT_EVENT_STREAM);
+ return request.rxSend();
+ })
+ .flatMapPublisher(response -> {
+ assertThat(response.statusCode()).isEqualTo(200);
+ return response.toFlowable();
+ })
+ .filter(buffer -> !buffer.toString().startsWith("retry:") && !buffer.toString().startsWith(":"))
+ .test()
+ .awaitCount(1)
+ .assertValue(body -> {
+ responseCheckpoint.flag();
+ return true;
+ });
+ assertThat(vertxTestContext.awaitCompletion(30, TimeUnit.SECONDS)).isTrue();
+ MessageMetrics messageMetrics = messageMetricsRef.get();
+ assertThat(messageMetrics).isNotNull();
+ assertThat(messageMetrics.getGatewayLatencyMs()).isGreaterThan(2000);
+ }
+
+ void prepareReporter(final VertxTestContext vertxTestContext, final int flagCount) {
+ Checkpoint fakeReporterCheckpoint = vertxTestContext.checkpoint(flagCount);
+ fakeReporter = getBean(FakeReporter.class);
+ metricsRef = new AtomicReference<>();
+ messageMetricsRef = new AtomicReference<>();
+ fakeReporter.setReportableHandler(reportable -> {
+ fakeReporterCheckpoint.flag();
+ if (reportable instanceof Metrics) {
+ metricsRef.set((Metrics) reportable);
+ } else if (reportable instanceof MessageMetrics) {
+ messageMetricsRef.set((MessageMetrics) reportable);
+ }
+ });
+ }
+}
diff --git a/src/test/java/io/gravitee/policy/latency/LatencyPolicyTest.java b/src/test/java/io/gravitee/policy/latency/LatencyPolicyTest.java
deleted file mode 100644
index 3df4d3a6..00000000
--- a/src/test/java/io/gravitee/policy/latency/LatencyPolicyTest.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/**
- * Copyright (C) 2015 The Gravitee team (http://gravitee.io)
- *
- * Licensed 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 io.gravitee.policy.latency;
-
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import io.gravitee.el.TemplateContext;
-import io.gravitee.el.TemplateEngine;
-import io.gravitee.gateway.api.ExecutionContext;
-import io.gravitee.gateway.api.stream.exception.TransformationException;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.runners.MockitoJUnitRunner;
-
-/**
- * @author Azize ELAMRANI (azize.elamrani at graviteesource.com)
- * @author GraviteeSource Team
- */
-@RunWith(MockitoJUnitRunner.class)
-@Ignore
-public class LatencyPolicyTest {
-
- @Mock
- protected ExecutionContext executionContext;
-
- @Before
- public void init() {
- initMocks(this);
- }
-
- @Test
- public void shouldTransformInput() throws Exception {}
-
- @Test(expected = TransformationException.class)
- public void shouldThrowException() throws Exception {}
-
- private class MockTemplateEngine implements TemplateEngine {
-
- @Override
- public String convert(String s) {
- return s;
- }
-
- @Override
- public T getValue(String expression, Class clazz) {
- return null;
- }
-
- @Override
- public TemplateContext getTemplateContext() {
- return null;
- }
- }
-}
diff --git a/src/test/resources/apis/latency.json b/src/test/resources/apis/latency-v2.json
similarity index 100%
rename from src/test/resources/apis/latency.json
rename to src/test/resources/apis/latency-v2.json
diff --git a/src/test/resources/apis/latency-v4-message-publish.json b/src/test/resources/apis/latency-v4-message-publish.json
new file mode 100644
index 00000000..4c67a88b
--- /dev/null
+++ b/src/test/resources/apis/latency-v4-message-publish.json
@@ -0,0 +1,64 @@
+{
+ "id": "my-message-publish-api",
+ "name": "my-message-publish-api",
+ "apiVersion": "1.0",
+ "definitionVersion": "4.0.0",
+ "type": "message",
+ "listeners": [
+ {
+ "type": "http",
+ "paths": [
+ {
+ "path": "/test"
+ }
+ ],
+ "entrypoints": [
+ {
+ "type": "http-post"
+ }
+ ]
+ }
+ ],
+ "endpointGroups": [
+ {
+ "name": "default-group",
+ "type": "mock",
+ "endpoints": [
+ {
+ "name": "default",
+ "type": "mock",
+ "weight": 1,
+ "inheritConfiguration": false,
+ "configuration": {
+ "messageCount": 1,
+ "messageContent": "{ \"message\": \"hello\" }"
+ }
+ }
+ ]
+ }
+ ],
+ "flows": [
+ {
+ "name": "flow-1",
+ "enabled": true,
+ "publish": [
+ {
+ "name": "Latency policy",
+ "description": "",
+ "enabled": true,
+ "policy": "latency",
+ "configuration": {
+ "time": 2,
+ "timeUnit": "SECONDS"
+ }
+ }
+ ],
+ "request": [],
+ "response": [],
+ "subscribe": []
+ }
+ ],
+ "analytics" : {
+ "enabled " : true
+ }
+}
diff --git a/src/test/resources/apis/latency-v4-message-subscribe.json b/src/test/resources/apis/latency-v4-message-subscribe.json
new file mode 100644
index 00000000..2cab4082
--- /dev/null
+++ b/src/test/resources/apis/latency-v4-message-subscribe.json
@@ -0,0 +1,67 @@
+{
+ "id": "my-message-subscribe-api",
+ "name": "my-message-subscribe-api",
+ "apiVersion": "1.0",
+ "definitionVersion": "4.0.0",
+ "type": "message",
+ "listeners": [
+ {
+ "type": "http",
+ "paths": [
+ {
+ "path": "/test"
+ }
+ ],
+ "entrypoints": [
+ {
+ "type": "sse",
+ "configuration": {
+ "headersAsComment" : true
+ }
+ }
+ ]
+ }
+ ],
+ "endpointGroups": [
+ {
+ "name": "default-group",
+ "type": "mock",
+ "endpoints": [
+ {
+ "name": "default",
+ "type": "mock",
+ "weight": 1,
+ "inheritConfiguration": false,
+ "configuration": {
+ "messageContent": "{ \"message\": \"hello\" }",
+ "messageCount": 1
+ }
+ }
+ ]
+ }
+ ],
+ "flows": [
+ {
+ "name": "flow-1",
+ "enabled": true,
+ "subscribe": [
+ {
+ "name": "Latency policy",
+ "description": "",
+ "enabled": true,
+ "policy": "latency",
+ "configuration": {
+ "time": 2,
+ "timeUnit": "SECONDS"
+ }
+ }
+ ],
+ "request": [],
+ "response": [],
+ "publish": []
+ }
+ ],
+ "analytics" : {
+ "enabled " : true
+ }
+}
diff --git a/src/test/resources/apis/latency-v4-proxy.json b/src/test/resources/apis/latency-v4-proxy.json
new file mode 100644
index 00000000..d968cdb8
--- /dev/null
+++ b/src/test/resources/apis/latency-v4-proxy.json
@@ -0,0 +1,63 @@
+{
+ "id": "my-proxy-api",
+ "name": "my-proxy-api",
+ "apiVersion": "1.0",
+ "definitionVersion": "4.0.0",
+ "type": "proxy",
+ "listeners": [
+ {
+ "type": "http",
+ "paths": [
+ {
+ "path": "/test"
+ }
+ ],
+ "entrypoints": [
+ {
+ "type": "http-proxy"
+ }
+ ]
+ }
+ ],
+ "endpointGroups": [
+ {
+ "name": "default-group",
+ "type": "http-proxy",
+ "endpoints": [
+ {
+ "name": "default",
+ "type": "http-proxy",
+ "weight": 1,
+ "inheritConfiguration": false,
+ "configuration": {
+ "target": "http://localhost:8080/endpoint"
+ }
+ }
+ ]
+ }
+ ],
+ "flows": [
+ {
+ "name": "flow-1",
+ "enabled": true,
+ "request": [
+ {
+ "name": "Latency policy",
+ "description": "",
+ "enabled": true,
+ "policy": "latency",
+ "configuration": {
+ "time": 2,
+ "timeUnit": "SECONDS"
+ }
+ }
+ ],
+ "response": [],
+ "subscribe": [],
+ "publish": []
+ }
+ ],
+ "analytics" : {
+ "enabled " : true
+ }
+}
diff --git a/src/test/resources/logback-test.xml b/src/test/resources/logback-test.xml
index 3e842fd9..d5478327 100644
--- a/src/test/resources/logback-test.xml
+++ b/src/test/resources/logback-test.xml
@@ -10,12 +10,12 @@
-
+
-
+