TimeOUt-Policy : new policy creation
MODIFY
pom.xml
pom.xml
<module>repository</module>
<module>auth-3scale</module>
<module>custom-path-parser-demo</module>
+ <module>timeout-policy</module>
</modules>
<profiles>

ADD
timeout-policy/README.md
+ # timeout-policy
+
+A policy that changes the timeouts configuration for the api.
+
+The policy allows the user to adjust the timeout read and connect used by the HTTP Client when execute the query on the backend api.
+
+## How it works
+
+On receiving a request, the policy injects the values in the EndPointProperties. The Http Client read these values and use it. If no value is setting, the default value is used.
+The default values are defined in the apiman.properties :
+* apiman-gateway.connector-factory.http.timeouts.read
+* apiman-gateway.connector-factory.http.timeouts.connect
+
+## Author
+
+William Beck <william.beck.pro@gmail.com>

ADD
timeout-policy/pom.xml
+<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>io.apiman.plugins</groupId>
+ <artifactId>apiman-plugins</artifactId>
+ <version>2.0.1-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+ <artifactId>apiman-plugins-timeout-policy</artifactId>
+ <packaging>war</packaging>
+ <name>apiman-plugins-timeout-policy</name>
+ <dependencies>
+ <!-- apiman dependencies (must be excluded from the WAR) -->
+ <dependency>
+ <groupId>io.apiman</groupId>
+ <artifactId>apiman-gateway-engine-core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>io.apiman</groupId>
+ <artifactId>apiman-gateway-engine-policies</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <!-- Test Only -->
+ <dependency>
+ <groupId>io.apiman</groupId>
+ <artifactId>apiman-test-policies</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <resources>
+ <resource>
+ <filtering>false</filtering>
+ <directory>src/main/resources</directory>
+ </resource>
+ </resources>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-war-plugin</artifactId>
+ <configuration>
+ <failOnMissingWebXml>false</failOnMissingWebXml>
+ <webResources>
+ <resource>
+ <directory>src/main/apiman</directory>
+ <targetPath>META-INF/apiman</targetPath>
+ <filtering>true</filtering>
+ </resource>
+ </webResources>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
\ No newline at end of file

ADD
timeout-policy/src/main/apiman/plugin.json
+{
+ "frameworkVersion" : 1.0,
+ "name" : "Timeout Policy Plugin",
+ "description" : "This policy configures timeout for the back-end connection.",
+ "version" : "${project.version}"
+}
\ No newline at end of file

ADD
timeout-policy/src/main/apiman/policyDefs/schemas/timeout-policyDef.schema
+{
+ "title" : "Timeout Policy Configuration",
+ "description" : "Configure Timeout for API.",
+ "type" : "object",
+ "properties": {
+ "timeoutConnect": {
+ "type": "string",
+ "title": "Timeout Connect (ms)",
+ "default": "10000"
+ },
+ "timeoutRead": {
+ "type": "string",
+ "title": "Timeout Read (ms)",
+ "default": "30000"
+ }
+ }
+}
\ No newline at end of file

ADD
timeout-policy/src/main/apiman/policyDefs/timeout-policyDef.json
+{
+ "id" : "timeout-policy",
+ "name" : "Timeout Policy",
+ "description" : "Configures timeout for the back-end connection.",
+ "policyImpl" : "plugin:${project.groupId}:${project.artifactId}:${project.version}:${project.packaging}/io.apiman.plugins.timeoutpolicy.TimeoutPolicy",
+ "icon" : "bullhorn",
+ "formType" : "JsonSchema",
+ "templates" : [
+ {
+ "language": null,
+ "template": "Timeout Connect : @{timeoutConnect} ms, Timeout Read : @{timeoutRead} ms"
+ }
+ ],
+ "form" : "schemas/timeout-policyDef.schema"
+}
\ No newline at end of file

ADD
timeout-policy/src/main/java/io/apiman/plugins/timeoutpolicy/TimeoutPolicy.java
+package io.apiman.plugins.timeoutpolicy;
+
+import java.util.Map;
+
+import io.apiman.gateway.engine.beans.ApiRequest;
+import io.apiman.gateway.engine.policies.AbstractMappedPolicy;
+import io.apiman.gateway.engine.policy.IPolicyChain;
+import io.apiman.gateway.engine.policy.IPolicyContext;
+import io.apiman.plugins.timeoutpolicy.beans.TimeoutConfigBean;
+
+/**
+ *
+ * Set, overwrite timeout on backend request.
+ *
+ * @author William Beck {@literal <william.beck.pro@gmail.com>}
+ */
+public class TimeoutPolicy extends AbstractMappedPolicy<TimeoutConfigBean> {
+
+ @Override
+ protected Class<TimeoutConfigBean> getConfigurationClass() {
+ return TimeoutConfigBean.class;
+ }
+
+ @Override
+ protected void doApply(ApiRequest request, IPolicyContext context, TimeoutConfigBean config,
+ IPolicyChain<ApiRequest> chain) {
+ Map<String, String> mapPropertiesOfAPI = request.getApi().getEndpointProperties();
+ // update the endpoint properties. Use in the
+ // io.apiman.gateway.platforms.servlet.connectors.HttpApiConnection
+ String newTimeout = config.getTimeoutConnect();
+ if (newTimeout != null && newTimeout.length() > 0) {
+ mapPropertiesOfAPI.put("timeouts.connect", newTimeout);
+ }
+ newTimeout = config.getTimeoutRead();
+ if (newTimeout != null && newTimeout.length() > 0) {
+ mapPropertiesOfAPI.put("timeouts.read", newTimeout);
+ }
+ chain.doApply(request);
+ }
+
+}

ADD
timeout-policy/src/main/java/io/apiman/plugins/timeoutpolicy/beans/TimeoutConfigBean.java
+package io.apiman.plugins.timeoutpolicy.beans;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class TimeoutConfigBean {
+
+ @JsonProperty
+ private String timeoutConnect;
+
+ @JsonProperty
+ private String timeoutRead;
+
+ public String getTimeoutConnect() {
+ return timeoutConnect;
+ }
+
+ public void setTimeoutConnect(String timeoutConnect) {
+ this.timeoutConnect = timeoutConnect;
+ }
+
+ public String getTimeoutRead() {
+ return timeoutRead;
+ }
+
+ public void setTimeoutRead(String timeoutRead) {
+ this.timeoutRead = timeoutRead;
+ }
+
+}

ADD
timeout-policy/src/test/java/io/apiman/plugins/timeoutpolicy/TimeoutPolicyTest.java
+package io.apiman.plugins.timeoutpolicy;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
+
+import java.util.HashMap;
+
+import org.junit.Test;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import io.apiman.gateway.engine.beans.ApiRequest;
+import io.apiman.gateway.engine.beans.ApiResponse;
+import io.apiman.plugins.timeoutpolicy.beans.TimeoutConfigBean;
+import io.apiman.test.policies.ApimanPolicyTest;
+import io.apiman.test.policies.BackEndApi;
+import io.apiman.test.policies.Configuration;
+import io.apiman.test.policies.IPolicyTestBackEndApi;
+import io.apiman.test.policies.PolicyFailureError;
+import io.apiman.test.policies.PolicyTestBackEndApiResponse;
+import io.apiman.test.policies.PolicyTestRequest;
+import io.apiman.test.policies.PolicyTestRequestType;
+import io.apiman.test.policies.PolicyTestResponse;
+import io.apiman.test.policies.TestingPolicy;
+
+/**
+ * @author William Beck {@literal <william.beck.pro@gmail.com>}
+ */
+@TestingPolicy(TimeoutPolicy.class)
+public class TimeoutPolicyTest extends ApimanPolicyTest {
+ private TimeoutPolicy timeoutPolicy = new TimeoutPolicy();
+
+ /**
+ * Control the type of the config bean
+ */
+ @Test
+ @Configuration("{\"timeoutConnect\" : \"1\", \"timeoutRead\" : \"1\" }")
+ public void shouldReturnTimeoutConfigBean_onGetConfigurationClass() {
+ // WHEN retrieving the configuration class
+ Class<?> confClass = timeoutPolicy.getConfigurationClass();
+
+ // THEN it must be the payload bean config
+ assertEquals(TimeoutConfigBean.class, confClass);
+ }
+
+ /**
+ * Control the parse system for the configuration
+ */
+ @Test
+ @Configuration("{\"timeoutConnect\" : \"1\", \"timeoutRead\" : \"2\" }")
+ public void shouldReturnConfigBeanWithValue_onParseConfiguration() throws PolicyFailureError, Throwable {
+ Configuration config = this.getClass()
+ .getMethod("shouldReturnConfigBeanWithValue_onParseConfiguration")
+ .getAnnotation(Configuration.class);
+
+ // WHEN parse the configuration
+ TimeoutConfigBean policyConfigBean = timeoutPolicy.parseConfiguration(config.value());
+
+ // THEN the bean's value equals the test config
+ HashMap<?, ?> configMap = new ObjectMapper().readValue(config.value(), HashMap.class);
+ assertEquals(configMap.get("timeoutConnect"), policyConfigBean.getTimeoutConnect());
+ assertEquals(configMap.get("timeoutRead"), policyConfigBean.getTimeoutRead());
+ }
+
+ /**
+ * Control the normal execution
+ */
+ @Test
+ @Configuration("{\"timeoutConnect\" : \"1\", \"timeoutRead\" : \"2\" }")
+ @BackEndApi(EndPointPropertiesEcho.class)
+ public void shouldExecute_onSimpleConfiguration() throws PolicyFailureError, Throwable {
+ // WHEN Execute the policy
+ PolicyTestResponse response = null;
+ try {
+ response = send(PolicyTestRequest.build(PolicyTestRequestType.GET, "/some/resource"));
+ } catch (PolicyFailureError ex) {
+ fail("Configuration error");
+ }
+ // THEN timeouts are set
+ HashMap<?, ?> responseMap = new ObjectMapper().readValue(response.body(), HashMap.class);
+ assertEquals("timeoutConnect", "1", responseMap.get("timeouts.connect"));
+ assertEquals("timeoutRead", "2", responseMap.get("timeouts.read"));
+ }
+
+ /**
+ * Execute with no value
+ */
+ @Test
+ @Configuration("{}")
+ @BackEndApi(EndPointPropertiesEcho.class)
+ public void shouldsettingNothing_onNoValue() throws PolicyFailureError, Throwable {
+ // WHEN Execute the policy
+ PolicyTestResponse response = null;
+ try {
+ response = send(PolicyTestRequest.build(PolicyTestRequestType.GET, "/some/resource"));
+ } catch (PolicyFailureError ex) {
+ fail("Configuration error");
+ }
+ // THEN timeouts are not set
+ HashMap<?, ?> responseMap = new ObjectMapper().readValue(response.body(), HashMap.class);
+ assertFalse("timeoutConnect", responseMap.containsKey("timeouts.connect"));
+ assertFalse("timeoutRead", responseMap.containsKey("timeouts.read"));
+ }
+
+ /**
+ * Execute with empty value
+ */
+ @Test
+ @Configuration("{\"timeoutConnect\" : \"\", \"timeoutRead\" : \"\" }")
+ @BackEndApi(EndPointPropertiesEcho.class)
+ public void shouldsettingNothing_onEmptyValue() throws PolicyFailureError, Throwable {
+ // WHEN Execute the policy
+ PolicyTestResponse response = null;
+ try {
+ response = send(PolicyTestRequest.build(PolicyTestRequestType.GET, "/some/resource"));
+ } catch (PolicyFailureError ex) {
+ fail("Configuration error");
+ }
+ // THEN timeouts are not set
+ HashMap<?, ?> responseMap = new ObjectMapper().readValue(response.body(), HashMap.class);
+ assertFalse("timeoutConnect", responseMap.containsKey("timeouts.connect"));
+ assertFalse("timeoutRead", responseMap.containsKey("timeouts.read"));
+ }
+
+ /**
+ *
+ * Return the EndPointProperties in response body
+ *
+ */
+ public static final class EndPointPropertiesEcho implements IPolicyTestBackEndApi {
+ @Override
+ public PolicyTestBackEndApiResponse invoke(ApiRequest request, byte[] requestBody) {
+ ApiResponse response = new ApiResponse();
+ response.setMessage("OK");
+ response.setCode(200);
+ String responseBody = "{}";
+ try {
+ responseBody = new ObjectMapper()
+ .writeValueAsString(request.getApi().getEndpointProperties());
+ } catch (JsonProcessingException e) {
+ e.printStackTrace();
+ }
+ return new PolicyTestBackEndApiResponse(response, responseBody);
+ }
+ }
+}

TimeOUt-Policy : new policy creation